Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -21,7 +21,7 @@ public class GenerateCode {
|
||||
|
||||
private static final String TARGET_DIR = "D://code";
|
||||
|
||||
private static final String DB_URL = "jdbc:mysql://192.168.1.24:13306/pqsinfo_ln";
|
||||
private static final String DB_URL = "jdbc:mysql://192.168.1.103:13306/pqsinfo_zl";
|
||||
// private static final String DB_URL = "jdbc:oracle:thin:@192.168.1.170:1521:pqsbase";
|
||||
|
||||
private static final String USERNAME = "root";
|
||||
@@ -30,8 +30,8 @@ public class GenerateCode {
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<Module> modules = Stream.of(
|
||||
new Module("cdf", "com.njcn.device", "", Stream.of(
|
||||
"pq_icd_path"
|
||||
new Module("xy", "com.njcn.csdevice", "", Stream.of(
|
||||
"cs_alarm_set"
|
||||
).collect(Collectors.toList()), "")
|
||||
).collect(Collectors.toList());
|
||||
generateJavaFile(modules);
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package com.njcn.echarts.json;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.hutool.json.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.njcn.echarts.pojo.bo.TolerateData;
|
||||
import com.njcn.echarts.pojo.constant.PicCommonData;
|
||||
import org.icepear.echarts.Option;
|
||||
@@ -29,7 +25,6 @@ import org.icepear.echarts.render.Engine;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -43,6 +38,17 @@ import java.util.stream.Collectors;
|
||||
public class LineGenerator {
|
||||
|
||||
private final static Engine ENGINE = new Engine();
|
||||
// null段在图表上的固定视觉宽度(数据点数量)
|
||||
private static final int NULL_PLACEHOLDER_COUNT = 90;
|
||||
// 压缩结果
|
||||
private static class CompressedResult {
|
||||
// 压缩后的数据
|
||||
List<List<Float>> data;
|
||||
// 每个null段的[起始索引, 结束索引](占位点范围)
|
||||
List<int[]> nullRegionIndices;
|
||||
// null段边界处的真实x值(entry, exit交替)
|
||||
List<Float> nullBoundaryRealXValues;
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
@@ -529,6 +535,478 @@ public class LineGenerator {
|
||||
return ENGINE.renderJsonOption(instantOption);
|
||||
}
|
||||
|
||||
public static String generateWaveOption(String title, List<List<Float>> aValue, List<List<Float>> bValue, List<List<Float>> cValue, String unit, Float max, Float min, String a, String b, String c, List<String> colors, Boolean isOpen, Double lastTime, List<Float> sharedNullBoundaryXValues, Integer nPush) {
|
||||
DecimalFormat df1 = new DecimalFormat("#.00");
|
||||
// 压缩null数据:如果有共享边界则用共享边界,否则独立压缩
|
||||
CompressedResult aCompressed;
|
||||
CompressedResult bCompressed;
|
||||
CompressedResult cCompressed;
|
||||
if (sharedNullBoundaryXValues != null && !sharedNullBoundaryXValues.isEmpty()) {
|
||||
aCompressed = compressNullDataWithSharedBoundaries(aValue, sharedNullBoundaryXValues);
|
||||
bCompressed = compressNullDataWithSharedBoundaries(bValue, sharedNullBoundaryXValues);
|
||||
cCompressed = compressNullDataWithSharedBoundaries(cValue, sharedNullBoundaryXValues);
|
||||
} else {
|
||||
aCompressed = compressNullData(aValue);
|
||||
bCompressed = compressNullData(bValue);
|
||||
cCompressed = compressNullData(cValue);
|
||||
}
|
||||
|
||||
Option instantOption = new Option();
|
||||
//取消渲染动画
|
||||
instantOption.setAnimation(false);
|
||||
//背景色
|
||||
instantOption.setBackgroundColor(PicCommonData.PIC_BACK_COLOR);
|
||||
//标题
|
||||
instantOption.setTitle(new Title()
|
||||
.setText(title)
|
||||
.setLeft(PicCommonData.CENTER)
|
||||
.setTextStyle(new Label().setFontSize(14))
|
||||
);
|
||||
//配置颜色
|
||||
instantOption.setColor(colors.toArray(new String[colors.size()]));
|
||||
//配置图例
|
||||
if (isOpen) {
|
||||
instantOption.setLegend(new Legend().setData(new String[]{a, b}).setLeft("10px"));
|
||||
} else {
|
||||
instantOption.setLegend(new Legend().setData(new String[]{a, b, c}).setLeft("10px"));
|
||||
}
|
||||
//上下左右的图内间距
|
||||
instantOption.setGrid(new Grid().setTop("60px").setLeft("70px").setRight("40px").setBottom("10%"));
|
||||
//横坐标(使用压缩后的数据)
|
||||
instantOption.setXAxis(new ValueAxis().setName("ms")
|
||||
.setSplitLine(new SplitLine().setShow(false))
|
||||
.setAxisLine(new AxisLine().setOnZero(false))
|
||||
.setType("category")
|
||||
.setMinInterval(1)
|
||||
);
|
||||
//纵坐标
|
||||
instantOption.setYAxis(new ValueAxis()
|
||||
.setName(unit)
|
||||
.setMax(df1.format(max * 1.1))
|
||||
.setMin(df1.format(min * 1.1))
|
||||
.setPosition(PicCommonData.LEFT)
|
||||
.setAxisLine(new AxisLine().setShow(false).setOnZero(false))
|
||||
);
|
||||
//数据信息(使用压缩后的数据)
|
||||
LineSeries aSeries = new LineSeries()
|
||||
.setName(a)
|
||||
.setSmooth(true)
|
||||
.setSymbol("none")
|
||||
.setConnectNulls(false)
|
||||
.setData(aCompressed.data);
|
||||
LineSeries bSeries = new LineSeries()
|
||||
.setName(b)
|
||||
.setSmooth(true)
|
||||
.setSymbol("none")
|
||||
.setConnectNulls(false)
|
||||
.setData(bCompressed.data);
|
||||
LineSeries cSeries = new LineSeries()
|
||||
.setName(c)
|
||||
.setSmooth(true)
|
||||
.setSymbol("none")
|
||||
.setConnectNulls(false)
|
||||
.setData(cCompressed.data);
|
||||
instantOption.setSeries(new LineSeries[]{aSeries, bSeries, cSeries});
|
||||
|
||||
// 渲染基础JSON
|
||||
String jsonStr = ENGINE.renderJsonOption(instantOption);
|
||||
cn.hutool.json.JSONObject optionJson = JSONUtil.parseObj(jsonStr);
|
||||
cn.hutool.json.JSONArray seriesArray = optionJson.getJSONArray("series");
|
||||
|
||||
// 隐藏x轴默认标签,设置boundaryGap=false,只通过markLine显示关键节点
|
||||
Object xAxisRaw = optionJson.get("xAxis");
|
||||
cn.hutool.json.JSONObject xAxisJson;
|
||||
if (xAxisRaw instanceof cn.hutool.json.JSONArray) {
|
||||
xAxisJson = ((cn.hutool.json.JSONArray) xAxisRaw).getJSONObject(0);
|
||||
} else {
|
||||
xAxisJson = (cn.hutool.json.JSONObject) xAxisRaw;
|
||||
}
|
||||
cn.hutool.json.JSONObject axisLabelJson = new cn.hutool.json.JSONObject();
|
||||
axisLabelJson.set("show", false);
|
||||
xAxisJson.set("axisLabel", axisLabelJson);
|
||||
xAxisJson.set("boundaryGap", false);
|
||||
|
||||
// 添加null markLine/markArea(基于压缩后的数据)
|
||||
addNullMarkLinesForCategoryAxis(seriesArray.getJSONObject(0), aCompressed, lastTime);
|
||||
addNullMarkLinesForCategoryAxis(seriesArray.getJSONObject(1), bCompressed, lastTime);
|
||||
addNullMarkLinesForCategoryAxis(seriesArray.getJSONObject(2), cCompressed, lastTime);
|
||||
// 在第一个series追加自定义横坐标标签markLine
|
||||
addAxisLabelMarkLines(seriesArray.getJSONObject(0), aCompressed, lastTime, nPush);
|
||||
return optionJson.toString();
|
||||
}
|
||||
|
||||
/** 提取null边界的真实x值,供其他数据集共享使用 */
|
||||
public static List<Float> extractNullBoundaryXValues(List<List<Float>> data) {
|
||||
List<Integer> boundaries = findNullSegmentBoundaryIndices(data);
|
||||
List<Float> xValues = new ArrayList<>();
|
||||
for (int idx : boundaries) {
|
||||
xValues.add(data.get(idx).get(0));
|
||||
}
|
||||
return xValues;
|
||||
}
|
||||
|
||||
/** 使用共享的null边界x值来压缩数据,确保不同数据集压缩结果结构一致 */
|
||||
private static CompressedResult compressNullDataWithSharedBoundaries(List<List<Float>> originalData, List<Float> sharedBoundaryXValues) {
|
||||
if (sharedBoundaryXValues == null || sharedBoundaryXValues.isEmpty()) {
|
||||
return compressNullData(originalData);
|
||||
}
|
||||
|
||||
// 用共享的x值找到本数据集中对应的索引
|
||||
List<Integer> boundaries = new ArrayList<>();
|
||||
for (Float x : sharedBoundaryXValues) {
|
||||
boundaries.add(findClosestValidIndex(originalData, x));
|
||||
}
|
||||
|
||||
CompressedResult result = new CompressedResult();
|
||||
result.data = new ArrayList<>();
|
||||
result.nullRegionIndices = new ArrayList<>();
|
||||
result.nullBoundaryRealXValues = new ArrayList<>(sharedBoundaryXValues);
|
||||
|
||||
int srcIdx = 0;
|
||||
for (int b = 0; b < boundaries.size(); b += 2) {
|
||||
int entryBoundary = boundaries.get(b);
|
||||
int exitBoundary = boundaries.get(b + 1);
|
||||
|
||||
while (srcIdx <= entryBoundary) {
|
||||
result.data.add(originalData.get(srcIdx));
|
||||
srcIdx++;
|
||||
}
|
||||
|
||||
int nullStartIdx = result.data.size();
|
||||
float entryX = sharedBoundaryXValues.get(b);
|
||||
float exitX = sharedBoundaryXValues.get(b + 1);
|
||||
|
||||
for (int p = 0; p < NULL_PLACEHOLDER_COUNT; p++) {
|
||||
List<Float> point = new ArrayList<>();
|
||||
point.add(entryX + (exitX - entryX) * p / NULL_PLACEHOLDER_COUNT);
|
||||
point.add(null);
|
||||
result.data.add(point);
|
||||
}
|
||||
|
||||
int nullEndIdx = result.data.size() - 1;
|
||||
result.nullRegionIndices.add(new int[]{nullStartIdx, nullEndIdx});
|
||||
|
||||
srcIdx = exitBoundary;
|
||||
}
|
||||
|
||||
while (srcIdx < originalData.size()) {
|
||||
result.data.add(originalData.get(srcIdx));
|
||||
srcIdx++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 压缩null数据:删除所有null数据点,替换为固定NULL_PLACEHOLDER_COUNT个占位点
|
||||
* 这样null段在图表上永远只占固定视觉宽度,前后有效数据正常展开
|
||||
*/
|
||||
private static CompressedResult compressNullData(List<List<Float>> originalData) {
|
||||
List<Integer> boundaries = findNullSegmentBoundaryIndices(originalData);
|
||||
|
||||
CompressedResult result = new CompressedResult();
|
||||
result.data = new ArrayList<>();
|
||||
result.nullRegionIndices = new ArrayList<>();
|
||||
result.nullBoundaryRealXValues = new ArrayList<>();
|
||||
|
||||
if (boundaries.isEmpty()) {
|
||||
result.data.addAll(originalData);
|
||||
return result;
|
||||
}
|
||||
|
||||
int srcIdx = 0;
|
||||
for (int b = 0; b < boundaries.size(); b += 2) {
|
||||
// 最后一个有效点的索引(null段入口边界)
|
||||
int entryBoundary = boundaries.get(b);
|
||||
// 第一个恢复有效点的索引(null段出口边界)
|
||||
int exitBoundary = boundaries.get(b + 1);
|
||||
|
||||
// 复制有效数据:从srcIdx到entryBoundary
|
||||
while (srcIdx <= entryBoundary) {
|
||||
result.data.add(originalData.get(srcIdx));
|
||||
srcIdx++;
|
||||
}
|
||||
|
||||
// 记录边界真实x值
|
||||
result.nullBoundaryRealXValues.add(originalData.get(entryBoundary).get(0));
|
||||
result.nullBoundaryRealXValues.add(originalData.get(exitBoundary).get(0));
|
||||
|
||||
// 插入固定数量的null占位点
|
||||
int nullStartIdx = result.data.size();
|
||||
float entryX = originalData.get(entryBoundary).get(0);
|
||||
float exitX = originalData.get(exitBoundary).get(0);
|
||||
|
||||
for (int p = 0; p < NULL_PLACEHOLDER_COUNT; p++) {
|
||||
List<Float> point = new ArrayList<>();
|
||||
point.add(entryX + (exitX - entryX) * p / NULL_PLACEHOLDER_COUNT);
|
||||
point.add(null);
|
||||
result.data.add(point);
|
||||
}
|
||||
|
||||
int nullEndIdx = result.data.size() - 1;
|
||||
result.nullRegionIndices.add(new int[]{nullStartIdx, nullEndIdx});
|
||||
|
||||
// 跳过原始null数据点,srcIdx跳到exitBoundary
|
||||
srcIdx = exitBoundary;
|
||||
}
|
||||
|
||||
// 复制最后一个null段之后的剩余有效数据
|
||||
while (srcIdx < originalData.size()) {
|
||||
result.data.add(originalData.get(srcIdx));
|
||||
srcIdx++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在压缩数据中查找最接近目标x值的有效数据点索引(跳过null点)
|
||||
*/
|
||||
private static int findClosestValidIndex(List<List<Float>> data, float targetX) {
|
||||
int closestIdx = -1;
|
||||
float minDiff = Float.MAX_VALUE;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
Float y = data.get(i).get(1);
|
||||
if (y == null) continue;
|
||||
float diff = Math.abs(data.get(i).get(0) - targetX);
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
closestIdx = i;
|
||||
}
|
||||
}
|
||||
return closestIdx;
|
||||
}
|
||||
|
||||
private static void addAxisLabelMarkLines(cn.hutool.json.JSONObject seriesObj, CompressedResult compressed, Double lastTime, Integer nPush) {
|
||||
boolean hasNull = !compressed.nullRegionIndices.isEmpty();
|
||||
|
||||
// 收集需要标注的横坐标位置:[数据索引, 显示的时间值]
|
||||
List<int[]> labelPositions = new ArrayList<>();
|
||||
// 开头ms(始终显示)
|
||||
labelPositions.add(new int[]{findClosestValidIndex(compressed.data, -nPush), -nPush});
|
||||
// 0ms(始终显示)
|
||||
labelPositions.add(new int[]{findClosestValidIndex(compressed.data, 0f), 0});
|
||||
// npush ms(仅在有null时显示)
|
||||
if (hasNull) {
|
||||
labelPositions.add(new int[]{findClosestValidIndex(compressed.data, nPush), nPush});
|
||||
}
|
||||
// null段边界真实时间值(仅在有null时显示)
|
||||
if (hasNull) {
|
||||
for (Float realX : compressed.nullBoundaryRealXValues) {
|
||||
int idx = findClosestValidIndex(compressed.data, realX);
|
||||
if (idx >= 0) {
|
||||
labelPositions.add(new int[]{idx, (int) Math.round(realX)});
|
||||
}
|
||||
}
|
||||
}
|
||||
// lastTime(始终显示)
|
||||
if (lastTime != null) {
|
||||
labelPositions.add(new int[]{findClosestValidIndex(compressed.data, lastTime.floatValue()), lastTime.intValue()});
|
||||
}
|
||||
|
||||
// 最后一组数据(始终显示,数据结束位置横坐标)
|
||||
if (lastTime != null) {
|
||||
float lastTime20 = lastTime.floatValue() + nPush;
|
||||
int lastTime20Idx = findClosestValidIndex(compressed.data, lastTime20);
|
||||
if (lastTime20Idx >= 0) {
|
||||
labelPositions.add(new int[]{(lastTime20Idx-2), (int) Math.round(lastTime20)});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取或创建markLine
|
||||
cn.hutool.json.JSONObject existingMarkLine = seriesObj.getJSONObject("markLine");
|
||||
cn.hutool.json.JSONArray markLineData;
|
||||
|
||||
if (existingMarkLine == null) {
|
||||
// 无null的情况:自己创建markLine,包含虚线和横坐标标签
|
||||
markLineData = new cn.hutool.json.JSONArray();
|
||||
|
||||
// x=0 虚线
|
||||
int zeroIdx = findClosestValidIndex(compressed.data, 0f);
|
||||
if (zeroIdx >= 0) {
|
||||
cn.hutool.json.JSONObject zeroLine = new cn.hutool.json.JSONObject();
|
||||
zeroLine.set("xAxis", zeroIdx);
|
||||
cn.hutool.json.JSONObject zeroLabel = new cn.hutool.json.JSONObject();
|
||||
zeroLabel.set("show", false);
|
||||
zeroLine.set("label", zeroLabel);
|
||||
markLineData.add(zeroLine);
|
||||
}
|
||||
// lastTime 虚线
|
||||
if (lastTime != null) {
|
||||
int lastTimeIdx = findClosestValidIndex(compressed.data, lastTime.floatValue());
|
||||
if (lastTimeIdx >= 0) {
|
||||
cn.hutool.json.JSONObject lastTimeLine = new cn.hutool.json.JSONObject();
|
||||
lastTimeLine.set("xAxis", lastTimeIdx);
|
||||
cn.hutool.json.JSONObject lastTimeLabelObj = new cn.hutool.json.JSONObject();
|
||||
lastTimeLabelObj.set("show", false);
|
||||
lastTimeLine.set("label", lastTimeLabelObj);
|
||||
markLineData.add(lastTimeLine);
|
||||
}
|
||||
}
|
||||
|
||||
// 先收集所有标签到markLineData
|
||||
for (int[] pos : labelPositions) {
|
||||
cn.hutool.json.JSONObject line = new cn.hutool.json.JSONObject();
|
||||
line.set("xAxis", pos[0]);
|
||||
cn.hutool.json.JSONObject lineLineStyle = new cn.hutool.json.JSONObject();
|
||||
lineLineStyle.set("width", 0);
|
||||
line.set("lineStyle", lineLineStyle);
|
||||
cn.hutool.json.JSONObject lineLabel = new cn.hutool.json.JSONObject();
|
||||
lineLabel.set("show", true);
|
||||
lineLabel.set("formatter", String.valueOf(pos[1]));
|
||||
lineLabel.set("position", "start");
|
||||
lineLabel.set("color", "#333");
|
||||
lineLabel.set("fontSize", 10);
|
||||
line.set("label", lineLabel);
|
||||
markLineData.add(line);
|
||||
}
|
||||
// 最后一次性设置到markLine和series
|
||||
existingMarkLine = new cn.hutool.json.JSONObject();
|
||||
existingMarkLine.set("symbol", new String[]{"none", "none"});
|
||||
cn.hutool.json.JSONObject lineStyle = new cn.hutool.json.JSONObject();
|
||||
lineStyle.set("type", "dashed");
|
||||
lineStyle.set("color", "#999");
|
||||
lineStyle.set("width", 1);
|
||||
existingMarkLine.set("lineStyle", lineStyle);
|
||||
cn.hutool.json.JSONObject globalLabel = new cn.hutool.json.JSONObject();
|
||||
globalLabel.set("show", false);
|
||||
existingMarkLine.set("label", globalLabel);
|
||||
existingMarkLine.set("data", markLineData);
|
||||
seriesObj.set("markLine", existingMarkLine);
|
||||
} else {
|
||||
// 有null的情况:追加标签到已有的markLine
|
||||
markLineData = existingMarkLine.getJSONArray("data");
|
||||
for (int[] pos : labelPositions) {
|
||||
cn.hutool.json.JSONObject line = new cn.hutool.json.JSONObject();
|
||||
line.set("xAxis", pos[0]);
|
||||
cn.hutool.json.JSONObject lineLineStyle = new cn.hutool.json.JSONObject();
|
||||
lineLineStyle.set("width", 0);
|
||||
line.set("lineStyle", lineLineStyle);
|
||||
cn.hutool.json.JSONObject lineLabel = new cn.hutool.json.JSONObject();
|
||||
lineLabel.set("show", true);
|
||||
lineLabel.set("formatter", String.valueOf(pos[1]));
|
||||
lineLabel.set("position", "start");
|
||||
lineLabel.set("color", "#333");
|
||||
lineLabel.set("fontSize", 10);
|
||||
line.set("label", lineLabel);
|
||||
markLineData.add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Integer> findNullSegmentBoundaryIndices(List<List<Float>> data) {
|
||||
List<Integer> indices = new ArrayList<>();
|
||||
boolean inNullSegment = false;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
Float y = data.get(i).get(1);
|
||||
if (y == null) {
|
||||
if (!inNullSegment) {
|
||||
indices.add(i > 0 ? i - 1 : i);
|
||||
inNullSegment = true;
|
||||
}
|
||||
} else {
|
||||
if (inNullSegment) {
|
||||
indices.add(i);
|
||||
inNullSegment = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inNullSegment) {
|
||||
indices.add(data.size() - 1);
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
private static void addNullMarkLinesForCategoryAxis(cn.hutool.json.JSONObject seriesObj, CompressedResult compressed, Double lastTime) {
|
||||
if (compressed.nullRegionIndices.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
cn.hutool.json.JSONArray markLineData = new cn.hutool.json.JSONArray();
|
||||
// x=0 虚线
|
||||
int zeroIdx = findClosestValidIndex(compressed.data, 0f);
|
||||
if (zeroIdx >= 0) {
|
||||
cn.hutool.json.JSONObject zeroLine = new cn.hutool.json.JSONObject();
|
||||
zeroLine.set("xAxis", zeroIdx);
|
||||
cn.hutool.json.JSONObject zeroLabel = new cn.hutool.json.JSONObject();
|
||||
zeroLabel.set("show", false);
|
||||
zeroLine.set("label", zeroLabel);
|
||||
markLineData.add(zeroLine);
|
||||
}
|
||||
// null段边界的垂直虚线
|
||||
for (int r = 0; r < compressed.nullRegionIndices.size(); r++) {
|
||||
int[] region = compressed.nullRegionIndices.get(r);
|
||||
int nullStartIdx = region[0]; // 第一个占位点索引
|
||||
int nullEndIdx = region[1]; // 最后一个占位点索引
|
||||
|
||||
// 入口虚线:nullStartIdx - 1(null前最后一个有效数据点)
|
||||
cn.hutool.json.JSONObject entryLine = new cn.hutool.json.JSONObject();
|
||||
entryLine.set("xAxis", nullStartIdx - 1);
|
||||
cn.hutool.json.JSONObject entryLabel = new cn.hutool.json.JSONObject();
|
||||
entryLabel.set("show", false);
|
||||
entryLine.set("label", entryLabel);
|
||||
markLineData.add(entryLine);
|
||||
|
||||
// 出口虚线:nullEndIdx(null后第一个有效数据点)
|
||||
cn.hutool.json.JSONObject exitLine = new cn.hutool.json.JSONObject();
|
||||
exitLine.set("xAxis", nullEndIdx);
|
||||
cn.hutool.json.JSONObject exitLabel = new cn.hutool.json.JSONObject();
|
||||
exitLabel.set("show", false);
|
||||
exitLine.set("label", exitLabel);
|
||||
markLineData.add(exitLine);
|
||||
}
|
||||
// 时间持续时间结束的数据点
|
||||
if (lastTime != null) {
|
||||
int lastTimeIdx = findClosestValidIndex(compressed.data, lastTime.floatValue());
|
||||
if (lastTimeIdx >= 0) {
|
||||
cn.hutool.json.JSONObject lastTimeLine = new cn.hutool.json.JSONObject();
|
||||
lastTimeLine.set("xAxis", lastTimeIdx);
|
||||
cn.hutool.json.JSONObject lastTimeLabel = new cn.hutool.json.JSONObject();
|
||||
lastTimeLabel.set("show", false);
|
||||
lastTimeLine.set("label", lastTimeLabel);
|
||||
markLineData.add(lastTimeLine);
|
||||
}
|
||||
}
|
||||
|
||||
cn.hutool.json.JSONObject markLine = new cn.hutool.json.JSONObject();
|
||||
markLine.set("symbol", new String[]{"none", "none"});
|
||||
cn.hutool.json.JSONObject lineStyle = new cn.hutool.json.JSONObject();
|
||||
lineStyle.set("type", "dashed");
|
||||
lineStyle.set("color", "#999");
|
||||
lineStyle.set("width", 1);
|
||||
markLine.set("lineStyle", lineStyle);
|
||||
cn.hutool.json.JSONObject globalLabel = new cn.hutool.json.JSONObject();
|
||||
globalLabel.set("show", false);
|
||||
markLine.set("label", globalLabel);
|
||||
markLine.set("data", markLineData);
|
||||
seriesObj.set("markLine", markLine);
|
||||
|
||||
// markArea - 灰色背景覆盖null区域(从入口边界到出口边界)
|
||||
cn.hutool.json.JSONArray markAreaData = new cn.hutool.json.JSONArray();
|
||||
for (int r = 0; r < compressed.nullRegionIndices.size(); r++) {
|
||||
int[] region = compressed.nullRegionIndices.get(r);
|
||||
int nullStartIdx = region[0];
|
||||
int nullEndIdx = region[1];
|
||||
|
||||
cn.hutool.json.JSONArray areaPair = new cn.hutool.json.JSONArray();
|
||||
cn.hutool.json.JSONObject start = new cn.hutool.json.JSONObject();
|
||||
start.set("xAxis", nullStartIdx - 1); // 从入口边界有效数据点
|
||||
cn.hutool.json.JSONObject end = new cn.hutool.json.JSONObject();
|
||||
end.set("xAxis", nullEndIdx); // 到出口边界有效数据点
|
||||
areaPair.add(start);
|
||||
areaPair.add(end);
|
||||
markAreaData.add(areaPair);
|
||||
}
|
||||
cn.hutool.json.JSONObject markArea = new cn.hutool.json.JSONObject();
|
||||
cn.hutool.json.JSONObject itemStyle = new cn.hutool.json.JSONObject();
|
||||
itemStyle.set("color", "rgba(180, 180, 180, 0.2)");
|
||||
itemStyle.set("borderWidth", 0);
|
||||
markArea.set("itemStyle", itemStyle);
|
||||
markArea.set("data", markAreaData);
|
||||
seriesObj.set("markArea", markArea);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param title 标题
|
||||
* @param values 数据值
|
||||
|
||||
@@ -10,9 +10,10 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author hongawen
|
||||
@@ -55,7 +56,6 @@ public class DrawPicUtil {
|
||||
return Objects.requireNonNull(picResult.getBody()).indexOf("image/png") > 0 ? picResult.getBody() : "";
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* @author hongawen
|
||||
* 绘制波形图
|
||||
@@ -77,6 +77,11 @@ public class DrawPicUtil {
|
||||
return drawPic(instantJson, width, height);
|
||||
}
|
||||
|
||||
public String drawWavePic(String title, List<List<Float>> aValue, List<List<Float>> bValue, List<List<Float>> cValue, String unit, Float max, Float min, String a, String b, String c, List<String> colors, Boolean isOpen, Double lastTime, List<Float> sharedNullBoundaryXValues,Integer nPush) {
|
||||
String instantJson = LineGenerator.generateWaveOption(title, aValue, bValue, cValue, unit, max, min, a, b, c, colors, isOpen, lastTime, sharedNullBoundaryXValues,nPush);
|
||||
return drawPic(instantJson, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* 绘制itic曲线图
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.njcn.event.file.component;
|
||||
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.utils.FileUtil;
|
||||
import com.njcn.echarts.json.LineGenerator;
|
||||
import com.njcn.echarts.pojo.constant.PicCommonData;
|
||||
import com.njcn.echarts.util.DrawPicUtil;
|
||||
import com.njcn.event.file.pojo.bo.WaveDataDetail;
|
||||
@@ -12,21 +11,17 @@ import com.njcn.event.file.pojo.dto.WaveDataDTO;
|
||||
import com.njcn.event.file.pojo.enums.WaveFileResponseEnum;
|
||||
import com.njcn.oss.constant.OssPath;
|
||||
import com.njcn.oss.utils.FileStorageUtil;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import sun.awt.image.BufferedImageGraphicsConfig;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author hongawen
|
||||
@@ -49,36 +44,39 @@ public class WavePicComponent {
|
||||
* @date 2023/9/21 15:32
|
||||
* @return String 文件地址
|
||||
*/
|
||||
public String generateInstantImageZl(List<WaveDataDetail> waveDataDetails) {
|
||||
public String generateInstantImageZl(Integer nPush, List<WaveDataDetail> waveDataDetails, Double lastTime) {
|
||||
String firstPic = null, secondPic = null, thirdPic = null, forthPic = null;
|
||||
WaveDataDetail waveDataDetail0 = waveDataDetails.get(0);
|
||||
// 从instantData提取null边界x值,作为共享参考(与Shun图保持一致)
|
||||
List<Float> sharedNullBoundaries = LineGenerator.extractNullBoundaryXValues(waveDataDetail0.getInstantData().getAValue());
|
||||
for (WaveDataDetail waveDataDetail : waveDataDetails) {
|
||||
if (waveDataDetail.getChannelName().toUpperCase().startsWith("SU")) {
|
||||
firstPic = drawPicUtil.drawWavePic("电压-电网侧", waveDataDetail.getInstantData().getAValue(),
|
||||
waveDataDetail.getInstantData().getBValue(), waveDataDetail.getInstantData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getInstantData().getMax(), waveDataDetail.getInstantData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen()
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(),lastTime,sharedNullBoundaries,nPush
|
||||
);
|
||||
} else if (waveDataDetail.getChannelName().toUpperCase().startsWith("SI")) {
|
||||
thirdPic = drawPicUtil.drawWavePic("电流-电网侧", waveDataDetail.getInstantData().getAValue(),
|
||||
waveDataDetail.getInstantData().getBValue(), waveDataDetail.getInstantData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getInstantData().getMax(), waveDataDetail.getInstantData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen()
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(),lastTime,sharedNullBoundaries,nPush
|
||||
);
|
||||
} else if (waveDataDetail.getChannelName().toUpperCase().startsWith("LU")) {
|
||||
secondPic = drawPicUtil.drawWavePic("电压-负载侧", waveDataDetail.getInstantData().getAValue(),
|
||||
waveDataDetail.getInstantData().getBValue(), waveDataDetail.getInstantData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getInstantData().getMax(), waveDataDetail.getInstantData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen()
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(),lastTime,sharedNullBoundaries,nPush
|
||||
);
|
||||
} else {
|
||||
forthPic = drawPicUtil.drawWavePic("电流-负载侧", waveDataDetail.getInstantData().getAValue(),
|
||||
waveDataDetail.getInstantData().getBValue(), waveDataDetail.getInstantData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getInstantData().getMax(), waveDataDetail.getInstantData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen()
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(),lastTime,sharedNullBoundaries,nPush
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -91,36 +89,39 @@ public class WavePicComponent {
|
||||
* @date 2023/9/21 15:32
|
||||
* @return String 文件地址
|
||||
*/
|
||||
public String generateRmsImageZl(List<WaveDataDetail> waveDataDetails) {
|
||||
public String generateRmsImageZl(Integer nPush, List<WaveDataDetail> waveDataDetails, Double lastTime) {
|
||||
String firstPic = null, secondPic = null, thirdPic = null, forthPic = null;
|
||||
WaveDataDetail waveDataDetail0 = waveDataDetails.get(0);
|
||||
// 从instantData提取null边界x值,作为共享参考(与Shun图保持一致)
|
||||
List<Float> sharedNullBoundaries = LineGenerator.extractNullBoundaryXValues(waveDataDetail0.getInstantData().getAValue());
|
||||
for (WaveDataDetail waveDataDetail : waveDataDetails) {
|
||||
if (waveDataDetail.getChannelName().toUpperCase().startsWith("SU")) {
|
||||
firstPic = drawPicUtil.drawWavePic("电压-电网侧", waveDataDetail.getRmsData().getAValue(),
|
||||
waveDataDetail.getRmsData().getBValue(), waveDataDetail.getRmsData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getRmsData().getMax(), waveDataDetail.getRmsData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen()
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(),lastTime,sharedNullBoundaries,nPush
|
||||
);
|
||||
} else if (waveDataDetail.getChannelName().toUpperCase().startsWith("SI")) {
|
||||
thirdPic = drawPicUtil.drawWavePic("电流-电网侧", waveDataDetail.getRmsData().getAValue(),
|
||||
waveDataDetail.getRmsData().getBValue(), waveDataDetail.getRmsData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getRmsData().getMax(), waveDataDetail.getRmsData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen()
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(),lastTime,sharedNullBoundaries,nPush
|
||||
);
|
||||
} else if (waveDataDetail.getChannelName().toUpperCase().startsWith("LU")) {
|
||||
secondPic = drawPicUtil.drawWavePic("电压-负载侧", waveDataDetail.getRmsData().getAValue(),
|
||||
waveDataDetail.getRmsData().getBValue(), waveDataDetail.getRmsData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getRmsData().getMax(), waveDataDetail.getRmsData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen()
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(),lastTime,sharedNullBoundaries,nPush
|
||||
);
|
||||
} else {
|
||||
forthPic = drawPicUtil.drawWavePic("电流-负载侧", waveDataDetail.getRmsData().getAValue(),
|
||||
waveDataDetail.getRmsData().getBValue(), waveDataDetail.getRmsData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getRmsData().getMax(), waveDataDetail.getRmsData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen()
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(),lastTime,sharedNullBoundaries,nPush
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -209,6 +210,47 @@ public class WavePicComponent {
|
||||
return picPath;
|
||||
}
|
||||
|
||||
/***
|
||||
* 绘制瞬时波形图 App端用来绘制图片,会去掉部分数据,只保留开始数据 结束数据
|
||||
* @author xy
|
||||
* @return String 文件地址
|
||||
*/
|
||||
public String generateImageShun(Integer nPush, List<WaveDataDetail> waveDataDetails, Double lastTime) {
|
||||
String picPath = null;
|
||||
WaveDataDetail waveDataDetail = waveDataDetails.get(0);
|
||||
// 从instantData提取null边界x值,作为共享参考
|
||||
List<Float> sharedNullBoundaries = LineGenerator.extractNullBoundaryXValues(waveDataDetail.getInstantData().getAValue());
|
||||
|
||||
String firstPic = drawPicUtil.drawWavePic(getTitle(waveDataDetail.getChannelName()), waveDataDetail.getInstantData().getAValue(),
|
||||
waveDataDetail.getInstantData().getBValue(), waveDataDetail.getInstantData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getInstantData().getMax(), waveDataDetail.getInstantData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(), lastTime, sharedNullBoundaries,nPush
|
||||
);
|
||||
String secondPic;
|
||||
if (waveDataDetails.size() == 1) {
|
||||
if (firstPic.contains(PicCommonData.PNG_PREFIX)) {
|
||||
firstPic = firstPic.replace(PicCommonData.PNG_PREFIX, "");
|
||||
}
|
||||
byte[] bytes = Base64.getDecoder().decode(firstPic);
|
||||
picPath = fileStorageUtil.uploadStream(new ByteArrayInputStream(bytes), OssPath.EVENT_WAVE_PIC, FileUtil.generateFileName("png"));
|
||||
} else if (waveDataDetails.size() == 2) {
|
||||
waveDataDetail = waveDataDetails.get(1);
|
||||
secondPic = drawPicUtil.drawWavePic(getTitle(waveDataDetail.getChannelName()), waveDataDetail.getInstantData().getAValue(),
|
||||
waveDataDetail.getInstantData().getBValue(), waveDataDetail.getInstantData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getInstantData().getMax(), waveDataDetail.getInstantData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(), lastTime, sharedNullBoundaries,nPush
|
||||
);
|
||||
picPath = composeImage(firstPic, secondPic);
|
||||
}
|
||||
return picPath;
|
||||
}
|
||||
|
||||
public String getTitle(String channelName) {
|
||||
return channelName.toUpperCase().startsWith("U1") ? "电压" : "电流";
|
||||
}
|
||||
|
||||
/***
|
||||
* 绘制RMS波形图
|
||||
* @author hongawen
|
||||
@@ -248,6 +290,44 @@ public class WavePicComponent {
|
||||
return picPath;
|
||||
}
|
||||
|
||||
/***
|
||||
* 绘制RMS波形图 App端用来绘制图片,会去掉部分数据,只保留开始数据 结束数据
|
||||
* @author xy
|
||||
* @return String 文件地址
|
||||
*/
|
||||
public String generateImageRms(Integer nPush, List<WaveDataDetail> waveDataDetails, Double lastTime) {
|
||||
String picPath = null;
|
||||
WaveDataDetail waveDataDetail = waveDataDetails.get(0);
|
||||
|
||||
// 从instantData提取null边界x值,作为共享参考(与Shun图保持一致)
|
||||
List<Float> sharedNullBoundaries = LineGenerator.extractNullBoundaryXValues(waveDataDetail.getInstantData().getAValue());
|
||||
|
||||
String firstPic = drawPicUtil.drawWavePic(getTitle(waveDataDetail.getChannelName()), waveDataDetail.getRmsData().getAValue(),
|
||||
waveDataDetail.getRmsData().getBValue(), waveDataDetail.getRmsData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getRmsData().getMax(), waveDataDetail.getRmsData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(), lastTime, sharedNullBoundaries,nPush
|
||||
);
|
||||
String secondPic;
|
||||
if (waveDataDetails.size() == 1) {
|
||||
if (firstPic.contains(PicCommonData.PNG_PREFIX)) {
|
||||
firstPic = firstPic.replace(PicCommonData.PNG_PREFIX, "");
|
||||
}
|
||||
byte[] bytes = Base64.getDecoder().decode(firstPic);
|
||||
picPath = fileStorageUtil.uploadStream(new ByteArrayInputStream(bytes), OssPath.EVENT_WAVE_PIC, FileUtil.generateFileName("png"));
|
||||
} else if (waveDataDetails.size() == 2) {
|
||||
waveDataDetail = waveDataDetails.get(1);
|
||||
secondPic = drawPicUtil.drawWavePic(getTitle(waveDataDetail.getChannelName()), waveDataDetail.getRmsData().getAValue(),
|
||||
waveDataDetail.getRmsData().getBValue(), waveDataDetail.getRmsData().getCValue(),
|
||||
waveDataDetail.getUnit(), waveDataDetail.getRmsData().getMax(), waveDataDetail.getRmsData().getMin(),
|
||||
waveDataDetail.getA(), waveDataDetail.getB(), waveDataDetail.getC(),
|
||||
waveDataDetail.getColors(), waveDataDetail.getIsOpen(), lastTime, sharedNullBoundaries,nPush
|
||||
);
|
||||
picPath = composeImage(firstPic, secondPic);
|
||||
}
|
||||
return picPath;
|
||||
}
|
||||
|
||||
/***
|
||||
* 合成图片
|
||||
* @author hongawen
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.njcn.event.file.pojo.dto.WaveDataDTO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author hongawen
|
||||
@@ -80,28 +81,49 @@ public class WaveUtil {
|
||||
//根据相别来确认标题的名称
|
||||
for (int m = 0; m < iPhase; m++) {
|
||||
if (waveTitle.get(iPhase * i + m + 1).substring(1).contains("A")) {
|
||||
float tmpShunFirstA = sunData.get(j).get(iPhase * i + m + 1) * xishu;
|
||||
shunFirstA = tmpShunFirstA;
|
||||
sAValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpShunFirstA);
|
||||
}});
|
||||
if (Objects.isNull(sunData.get(j).get(iPhase * i + m + 1))) {
|
||||
sAValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(null);
|
||||
}});
|
||||
} else {
|
||||
float tmpShunFirstA = sunData.get(j).get(iPhase * i + m + 1) * xishu;
|
||||
shunFirstA = tmpShunFirstA;
|
||||
sAValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpShunFirstA);
|
||||
}});
|
||||
}
|
||||
}
|
||||
if (waveTitle.get(iPhase * i + m + 1).substring(1).contains("B")) {
|
||||
float tmpShunFirstB = sunData.get(j).get(iPhase * i + m + 1) * xishu;
|
||||
shunFirstB = tmpShunFirstB;
|
||||
sBValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpShunFirstB);
|
||||
}});
|
||||
if (Objects.isNull(sunData.get(j).get(iPhase * i + m + 1))) {
|
||||
sBValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(null);
|
||||
}});
|
||||
} else {
|
||||
float tmpShunFirstB = sunData.get(j).get(iPhase * i + m + 1) * xishu;
|
||||
shunFirstB = tmpShunFirstB;
|
||||
sBValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpShunFirstB);
|
||||
}});
|
||||
}
|
||||
}
|
||||
if (waveTitle.get(iPhase * i + m + 1).substring(1).contains("C")) {
|
||||
float tmpShunFirstC = sunData.get(j).get(iPhase * i + m + 1) * xishu;
|
||||
shunFirstC = tmpShunFirstC;
|
||||
sCValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpShunFirstC);
|
||||
}});
|
||||
if (Objects.isNull(sunData.get(j).get(iPhase * i + m + 1))) {
|
||||
sCValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(null);
|
||||
}});
|
||||
} else {
|
||||
float tmpShunFirstC = sunData.get(j).get(iPhase * i + m + 1) * xishu;
|
||||
shunFirstC = tmpShunFirstC;
|
||||
sCValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpShunFirstC);
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
sfMax = getMax(sfMax, shunFirstA, shunFirstB, shunFirstC);
|
||||
@@ -124,28 +146,50 @@ public class WaveUtil {
|
||||
//根据相别来确认标题的名称
|
||||
for (int m = 0; m < iPhase; m++) {
|
||||
if (waveTitle.get(iPhase * i + m + 1).substring(1).contains("A")) {
|
||||
float tmpRmsFirstA = rmsData.get(k).get(iPhase * i + m + 1) * xishu;
|
||||
rmsFirstA = tmpRmsFirstA;
|
||||
rAValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpRmsFirstA);
|
||||
}});
|
||||
if (Objects.isNull(rmsData.get(k).get(iPhase * i + m + 1))) {
|
||||
rAValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(null);
|
||||
}});
|
||||
} else {
|
||||
float tmpRmsFirstA = rmsData.get(k).get(iPhase * i + m + 1) * xishu;
|
||||
rmsFirstA = tmpRmsFirstA;
|
||||
rAValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpRmsFirstA);
|
||||
}});
|
||||
}
|
||||
}
|
||||
if (waveTitle.get(iPhase * i + m + 1).substring(1).contains("B")) {
|
||||
float tmpRmsFirstB = rmsData.get(k).get(iPhase * i + m + 1) * xishu;
|
||||
rmsFirstB = tmpRmsFirstB;
|
||||
rBValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpRmsFirstB);
|
||||
}});
|
||||
if (Objects.isNull(rmsData.get(k).get(iPhase * i + m + 1))) {
|
||||
rBValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(null);
|
||||
}});
|
||||
} else {
|
||||
float tmpRmsFirstB = rmsData.get(k).get(iPhase * i + m + 1) * xishu;
|
||||
rmsFirstB = tmpRmsFirstB;
|
||||
rBValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpRmsFirstB);
|
||||
}});
|
||||
}
|
||||
|
||||
}
|
||||
if (waveTitle.get(iPhase * i + m + 1).substring(1).contains("C")) {
|
||||
float tmpRmsFirstC = rmsData.get(k).get(iPhase * i + m + 1) * xishu;
|
||||
rmsFirstC = tmpRmsFirstC;
|
||||
rCValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpRmsFirstC);
|
||||
}});
|
||||
if (Objects.isNull(rmsData.get(k).get(iPhase * i + m + 1))) {
|
||||
rCValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(null);
|
||||
}});
|
||||
} else {
|
||||
float tmpRmsFirstC = rmsData.get(k).get(iPhase * i + m + 1) * xishu;
|
||||
rmsFirstC = tmpRmsFirstC;
|
||||
rCValue.add(new ArrayList<Float>() {{
|
||||
add(x);
|
||||
add(tmpRmsFirstC);
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
rfMax = getMax(sfMax, rmsFirstA, rmsFirstB, rmsFirstC);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.njcn.web.utils;
|
||||
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.*;
|
||||
@@ -149,7 +152,8 @@ public class RestTemplateUtil {
|
||||
* @return ResponseEntity 响应对象封装类
|
||||
*/
|
||||
public static <T> ResponseEntity<T> post(String url, Object requestBody, Class<T> responseType) {
|
||||
return restTemplate.postForEntity(url, requestBody, responseType);
|
||||
Object actualBody = convertHutoolJsonToStandard(requestBody);
|
||||
return restTemplate.postForEntity(url, actualBody, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,7 +166,8 @@ public class RestTemplateUtil {
|
||||
* @return ResponseEntity 响应对象封装类
|
||||
*/
|
||||
public static <T> ResponseEntity<T> post(String url, Object requestBody, Class<T> responseType, Object... uriVariables) {
|
||||
return restTemplate.postForEntity(url, requestBody, responseType, uriVariables);
|
||||
Object actualBody = convertHutoolJsonToStandard(requestBody);
|
||||
return restTemplate.postForEntity(url, actualBody, responseType, uriVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,7 +180,21 @@ public class RestTemplateUtil {
|
||||
* @return ResponseEntity 响应对象封装类
|
||||
*/
|
||||
public static <T> ResponseEntity<T> post(String url, Object requestBody, Class<T> responseType, Map<String, ?> uriVariables) {
|
||||
return restTemplate.postForEntity(url, requestBody, responseType, uriVariables);
|
||||
Object actualBody = convertHutoolJsonToStandard(requestBody);
|
||||
return restTemplate.postForEntity(url, actualBody, responseType, uriVariables);
|
||||
}
|
||||
|
||||
private static Object convertHutoolJsonToStandard(Object requestBody) {
|
||||
if (requestBody instanceof JSONObject || requestBody instanceof JSONArray) {
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
return objectMapper.readValue(JSONUtil.toJsonStr(requestBody), Object.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("Hutool JSON转标准结构失败,尝试直接使用字符串", e);
|
||||
return requestBody;
|
||||
}
|
||||
}
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,9 @@ public class MonitorCommLedgerInfoDTO implements Serializable {
|
||||
|
||||
private String busBarName;
|
||||
|
||||
private String objName;
|
||||
|
||||
|
||||
private String voltageLevel;
|
||||
|
||||
private String shortCapacity;
|
||||
|
||||
@@ -88,7 +88,9 @@
|
||||
voltage.name busBarname,
|
||||
pq_voltage.scale voltageLevel,
|
||||
bd.name bdName,
|
||||
gd.name gdName
|
||||
gd.name gdName,
|
||||
supervision_user_report.project_name objName
|
||||
|
||||
from pq_dept_line pq_dept_line
|
||||
inner join pq_line point on pq_dept_line.line_id = point.id
|
||||
inner join pq_line_detail lineDetail on point.id = lineDetail.id
|
||||
@@ -98,6 +100,7 @@
|
||||
inner join pq_device device on dev.id = device.id
|
||||
inner join pq_line bd on dev.pid = bd.id
|
||||
inner join pq_line gd on bd.pid = gd.id
|
||||
left join supervision_user_report on lineDetail.Obj_Id = supervision_user_report.id
|
||||
where device.Dev_Model = 1
|
||||
and point.state = 1
|
||||
and device.Dev_Data_Type in
|
||||
|
||||
@@ -20,6 +20,13 @@ public interface CommMonitorEventReportService {
|
||||
|
||||
void getLineExport(ExportParam exportParam, LineDetailDataCommDTO lineDetailDataCommDTO, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 暂降事件报告
|
||||
* @param exportParam
|
||||
* @param lineDetailDataCommDTO
|
||||
* @param response
|
||||
*/
|
||||
void getWlLineExport(ExportParam exportParam, LineDetailDataCommDTO lineDetailDataCommDTO, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 暂态事件报告
|
||||
|
||||
@@ -479,6 +479,389 @@ public class CommMonitorEventReportServiceImpl implements CommMonitorEventReport
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getWlLineExport(ExportParam exportParam, LineDetailDataCommDTO lineDetailData, HttpServletResponse response) {
|
||||
//创建word文档(poi生成word)
|
||||
XWPFDocument doc = new XWPFDocument(); //创建Word文件
|
||||
//设置标题样式
|
||||
WordUtils.setHeadingStyle(doc);
|
||||
XWPFParagraph p = doc.createParagraph(); //新建一个段落
|
||||
//设置对齐
|
||||
p.setAlignment(ParagraphAlignment.CENTER);
|
||||
p.setVerticalAlignment(TextAlignment.CENTER);
|
||||
XWPFRun r = p.createRun();//创建段落文本
|
||||
r.addBreak();
|
||||
r.addBreak();
|
||||
r.addBreak();
|
||||
r.addBreak();
|
||||
r.addBreak();
|
||||
r.addBreak();
|
||||
r.setText("");
|
||||
r.setBold(true);//设置为粗体
|
||||
r.setFontSize(14);//字体大小
|
||||
r.addBreak();
|
||||
|
||||
p = doc.createParagraph(); //新建一个段落
|
||||
p.setAlignment(ParagraphAlignment.CENTER);
|
||||
XWPFRun r1 = p.createRun();//创建段落文本
|
||||
r1.setText("电压暂降事件分析报告");
|
||||
r1.setBold(true);//设置为粗体
|
||||
r1.setFontSize(36);//字体大小
|
||||
r1.addBreak();
|
||||
r1.addBreak();
|
||||
r1.addBreak();
|
||||
r1.addBreak();
|
||||
r1.addBreak();
|
||||
r1.addBreak();
|
||||
r1.addBreak();
|
||||
|
||||
p = doc.createParagraph(); //新建一个段落
|
||||
p.setAlignment(ParagraphAlignment.CENTER);
|
||||
XWPFRun r2 = p.createRun();//创建段落文本
|
||||
//获取当前时间
|
||||
Date date = new Date();
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy 年 MM 月 dd 日");
|
||||
String time = dateFormat.format(date);
|
||||
|
||||
r2.setText("日期: " + time);
|
||||
r2.setBold(true);//设置为粗体
|
||||
r2.setFontSize(14);//字体大小
|
||||
|
||||
r2.addBreak();
|
||||
r2.addBreak();
|
||||
r2.addBreak();
|
||||
|
||||
p = doc.createParagraph(); //新建一个段落
|
||||
p.setAlignment(ParagraphAlignment.CENTER);
|
||||
XWPFRun r3 = p.createRun();//创建段落文本
|
||||
r3.setText("电压暂降事件区域报告");
|
||||
r3.setFontSize(24);//字体大小
|
||||
|
||||
p = doc.createParagraph(); //新建一个段落
|
||||
p.setAlignment(ParagraphAlignment.LEFT);
|
||||
createTitle(doc, "1. 引言", "标题 1", 0, 15);
|
||||
|
||||
p = doc.createParagraph(); //新建一个段落
|
||||
p.setAlignment(ParagraphAlignment.BOTH);
|
||||
XWPFRun r5 = p.createRun();//创建段落文本
|
||||
r5.setText("对所选中区间内电压暂降事件进行分析,能够直观清晰查看响应的暂降事件信息。");
|
||||
r5.setFontSize(11);//字体大小
|
||||
|
||||
p = doc.createParagraph(); //新建一个段落
|
||||
p.setAlignment(ParagraphAlignment.LEFT);
|
||||
createTitle(doc, "2. 报告分析对象", "标题 1", 0, 15);
|
||||
|
||||
p = doc.createParagraph(); //新建一个段落
|
||||
p.setAlignment(ParagraphAlignment.BOTH);
|
||||
XWPFRun r7 = p.createRun();//创建段落文本
|
||||
r7.setText(exportParam.getLineName());
|
||||
r7.setFontSize(11);//字体大小
|
||||
|
||||
p = doc.createParagraph(); //新建一个段落
|
||||
p.setAlignment(ParagraphAlignment.LEFT);
|
||||
createTitle(doc, "3. 报告分析时间", "标题 1", 0, 15);
|
||||
|
||||
p = doc.createParagraph(); //新建一个段落
|
||||
p.setAlignment(ParagraphAlignment.BOTH);
|
||||
XWPFRun r9 = p.createRun();//创建段落文本
|
||||
r9.setText(exportParam.getSearchBeginTime() + " 至 " + exportParam.getSearchEndTime());
|
||||
r9.setFontSize(11);//字体大小
|
||||
|
||||
p = doc.createParagraph(); //新建一个段落
|
||||
p.setAlignment(ParagraphAlignment.LEFT);
|
||||
createTitle(doc, "4. 总汇信息", "标题 1", 0, 15);
|
||||
|
||||
//查询参数
|
||||
StatisticsParam param = new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(), exportParam.getFlag());
|
||||
//获取暂降原因字典
|
||||
List<DictData> reasonData = dicDataFeignClient.getDicDataByTypeName(DicDataTypeEnum.EVENT_REASON.getName()).getData();
|
||||
//获取暂降类型字典
|
||||
List<DictData> typeData = dicDataFeignClient.getDicDataByTypeName(DicDataTypeEnum.EVENT_TYPE.getName()).getData();
|
||||
//influxdb查询结果(pqs_eventdetail表)
|
||||
List<EventDetail> info = info(param, true);
|
||||
|
||||
//记录数
|
||||
int i = 1;
|
||||
//1.监测点信息
|
||||
if (exportParam.isXq()) {
|
||||
createTitle(doc, "4." + i + " 监测点信息", "标题 2", 200, 15);
|
||||
XWPFTable table = createTable(doc);
|
||||
XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
|
||||
insertRow(doc, table, centerParagraph, true, "项目", "描述");
|
||||
insertRow(doc, table, centerParagraph, false, "监测点名称", lineDetailData.getLineName());
|
||||
insertRow(doc, table, centerParagraph, false, "电压等级", lineDetailData.getScale());
|
||||
insertRow(doc, table, centerParagraph, false, "PT变比", lineDetailData.getPt());
|
||||
insertRow(doc, table, centerParagraph, false, "CT变比", lineDetailData.getCt());
|
||||
insertRow(doc, table, centerParagraph, false, "协议容量", lineDetailData.getDealCapacity() + "");
|
||||
insertRow(doc, table, centerParagraph, false, "设备容量", lineDetailData.getDevCapacity() + "");
|
||||
insertRow(doc, table, centerParagraph, false, "最小短路容量", lineDetailData.getShortCapacity() + "");
|
||||
insertRow(doc, table, centerParagraph, false, "接线方式", lineDetailData.getPtType());
|
||||
if (exportParam.getType() == 0) {
|
||||
insertRow(doc, table, centerParagraph, false, "基准容量", lineDetailData.getStandardCapacity() + "");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
//2.暂降事件暂降事件列表和暂降点图
|
||||
//2.1.判断列表和点图是否是要导出
|
||||
if (exportParam.isLb() || exportParam.isSjdF47() || exportParam.isSjdITIC()) {
|
||||
List<EventDetail> plot = eventReportService.getPlot(info, reasonData, typeData);
|
||||
//暂降事件列表
|
||||
if (exportParam.isLb()) {
|
||||
createTitle(doc, "4." + i + " 暂降事件列表", "标题 2", 200, 15);
|
||||
XWPFTable table = createTable(doc);
|
||||
XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
|
||||
insertRow(doc, table, centerParagraph, true, "序号", "暂降发生时刻", "暂降幅值(%)", "持续时间(s)", "暂降类型", "暂降原因", "严重度");
|
||||
for (int j = 0; j < plot.size(); j++) {
|
||||
EventDetail eventDetail = plot.get(j);
|
||||
String s = eventDetail.getStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
|
||||
|
||||
insertRow(doc, table, centerParagraph, false, j + 1 + "", s, BigDecimal.valueOf(eventDetail.getFeatureAmplitude() * 100).setScale(2, RoundingMode.HALF_UP).toString(), eventDetail.getDuration() + "", Objects.isNull(eventDetail.getAdvanceType()) ? "/" : eventDetail.getAdvanceType(), Objects.isNull(eventDetail.getAdvanceReason()) ? "/" : eventDetail.getAdvanceReason(), Objects.isNull(eventDetail.getSeverity()) ? "/" : eventDetail.getSeverity() + "");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
//暂降事件点图
|
||||
if (exportParam.isSjdF47() || exportParam.isSjdITIC()) {
|
||||
ArrayList<List<Double>> ass = getAss(plot);
|
||||
createTitle(doc, "4." + i + " 暂降事件点图", "标题 2", 200, 15);
|
||||
int two = 1;
|
||||
if (exportParam.isSjdITIC()) {
|
||||
createTitle(doc, "4." + i + "." + two + " ITIC 曲线", "标题 3", 400, 15);
|
||||
String itic = drawPicUtil.drawItic(ass);
|
||||
createPic(doc, itic, "ITIC曲线");
|
||||
two++;
|
||||
}
|
||||
if (exportParam.isSjdF47()) {
|
||||
createTitle(doc, "4." + i + "." + two + " F47 曲线", "标题 3", 400, 15);
|
||||
String f47 = drawPicUtil.drawF47(ass);
|
||||
createPic(doc, f47, "SEMI F47曲线");
|
||||
two++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//3.暂降密度
|
||||
if (exportParam.isMdbg() || exportParam.isMdtx()) {
|
||||
createTitle(doc, "4." + i + " 暂降密度", "标题 2", 200, 15);
|
||||
int two = 1;
|
||||
if (exportParam.isMdtx()) {
|
||||
createTitle(doc, "4." + i + "." + two + " 暂降密度点图", "标题 3", 400, 15);
|
||||
Integer[][] eventDensityData = eventReportService.getCoords(info);
|
||||
String str = drawPicUtil.drawEventDensity(eventDensityData);
|
||||
createPic(doc, str, "暂降密度图");
|
||||
two++;
|
||||
}
|
||||
if (exportParam.isMdbg()) {
|
||||
XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
|
||||
createTitle(doc, "4." + i + "." + two + " DISDIP 表格:国际发配电联盟(UNIPEDE)", "标题 3", 400, 15);
|
||||
// List<DISDIPVO> eventDisdip = eventAnalysisService.eventDisdip(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
|
||||
List<DISDIPVO> eventDisdip = eventReportService.eventDisdip(info);
|
||||
|
||||
XWPFTable table1 = createTable(doc);
|
||||
insertRow(doc, table1, centerParagraph, true, "剩余电压", "20ms", "100ms", "500ms", "1s", "3s", "20s", "60s", "180s");
|
||||
for (int j = 0; j < eventDisdip.size(); j++) {
|
||||
DISDIPVO disdipvo = eventDisdip.get(j);
|
||||
insertRow(doc, table1, centerParagraph, false, disdipvo.getName(), disdipvo.getTwentyMs(), disdipvo.getOneHundredMs(), disdipvo.getFiveHundredMs(), disdipvo.getOneS(), disdipvo.getThreeS(), disdipvo.getTwentyS(), disdipvo.getSixtyS(), disdipvo.getOneEightyS());
|
||||
}
|
||||
two++;
|
||||
createTitle(doc, "4." + i + "." + two + " IEC 61000-4-11:(用电终端的电压暂降抗度)", "标题 3", 400, 15);
|
||||
// List<IEC411VO> iec411VOS = eventAnalysisService.IEC411(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
|
||||
List<IEC411VO> iec411VOS = eventReportService.IEC411(info);
|
||||
XWPFTable table2 = createTable(doc);
|
||||
insertRow(doc, table2, centerParagraph, true, "剩余电压", "10~20ms", "20~100ms", "0.1~0.2s", "0.2~0.5s", "0.5~1s", ">1s");
|
||||
for (int j = 0; j < iec411VOS.size(); j++) {
|
||||
IEC411VO iec411VO = iec411VOS.get(j);
|
||||
insertRow(doc, table2, centerParagraph, false, iec411VO.getName(), iec411VO.getTenTwentyMs(), iec411VO.getTwentyOneHundredMs(), iec411VO.getZeroPiontOneTwoS(), iec411VO.getZeroPiontTwoFiveS(), iec411VO.getZeroPiontFive1S(), iec411VO.getGreater1S());
|
||||
}
|
||||
two++;
|
||||
createTitle(doc, "4." + i + "." + two + " IEC 61000-2-8:(公共电网电压暂降测量统计)", "标题 3", 400, 15);
|
||||
// List<IEC28VO> iec28VOS = eventAnalysisService.IEC28(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
|
||||
List<IEC28VO> iec28VOS = eventReportService.IEC28(info);
|
||||
XWPFTable table3 = createTable(doc);
|
||||
insertRow(doc, table3, centerParagraph, true, "剩余电压", "0.02~0.1s", "0.1~0.25s", "0.25~0.5s", "0.5s~1s", "1~3s", "3~20s", "20~60s", "60~180s");
|
||||
for (int j = 0; j < iec28VOS.size(); j++) {
|
||||
IEC28VO iec28VO = iec28VOS.get(j);
|
||||
insertRow(doc, table3, centerParagraph, false, iec28VO.getName(), iec28VO.getQ(), iec28VO.getW(), iec28VO.getE(), iec28VO.getR(), iec28VO.getT(), iec28VO.getY(), iec28VO.getU(), iec28VO.getI());
|
||||
}
|
||||
two++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
//4.暂降幅值概率分布
|
||||
if (exportParam.isGlfbfz() || exportParam.isGlfbsj()) {
|
||||
createTitle(doc, "4." + i + " 暂降幅值概率分布图", "标题 2", 200, 15);
|
||||
// ProbabilityVO probabilityVO = eventAnalysisService.getProbabilityDistribution(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
|
||||
ProbabilityVO probabilityVO = eventReportService.getProbabilityDistribution(info);
|
||||
int two = 1;
|
||||
if (exportParam.isGlfbfz()) {
|
||||
createTitle(doc, "4." + i + "." + two + " 暂降幅值的概率分函数", "标题 3", 400, 15);
|
||||
List<String> ybardata = probabilityVO.getPereventvalue();
|
||||
List<String> ylinedata = probabilityVO.getEventvalue();
|
||||
String fz = drawPicUtil.drawEventAmplitude(ylinedata, ybardata);
|
||||
createPic(doc, fz, "暂降幅值的概率分布函数");
|
||||
two++;
|
||||
}
|
||||
if (exportParam.isGlfbsj()) {
|
||||
createTitle(doc, "4." + i + "." + two + " 持续时间的概率分函数", "标题 3", 400, 15);
|
||||
List<String> ybardata = probabilityVO.getPersisttime();
|
||||
List<String> ylinedata = probabilityVO.getSisttime();
|
||||
String sj = drawPicUtil.drawPersistentTime(ylinedata, ybardata);
|
||||
createPic(doc, sj, "持续时间的概率分布函数");
|
||||
two++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
//5.月份统计
|
||||
if (exportParam.isTjbg() || exportParam.isTjtx()) {
|
||||
createTitle(doc, "4." + i + " 月份统计", "标题 2", 200, 15);
|
||||
int two = 1;
|
||||
List<TimeVO> reasonTypeTime = eventAnalysisService.getReasonTypeTime(param);
|
||||
//暂时时间端按月查询不能查询
|
||||
// List<TimeVO> reasonTypeTime = eventReportService.getReasonTypeTime(param,null);
|
||||
if (exportParam.isTjtx()) {
|
||||
createTitle(doc, "4." + i + "." + two + " 月份统计图", "标题 3", 400, 15);
|
||||
List<Integer> count = new ArrayList<>();
|
||||
List<String> name = new ArrayList<>();
|
||||
if (exportParam.getFlag() == 0) {
|
||||
for (TimeVO timeVO : reasonTypeTime) {
|
||||
name.add(timeVO.getMonth() + "");
|
||||
count.add(Integer.parseInt(timeVO.getTimes()));
|
||||
}
|
||||
} else {
|
||||
for (TimeVO timeVO : reasonTypeTime) {
|
||||
name.add(timeVO.getDay() + "");
|
||||
count.add(Integer.parseInt(timeVO.getTimes()));
|
||||
}
|
||||
}
|
||||
String yftj = drawPicUtil.drawMonth(name, count, reasonTypeTime.get(0).getYear(), exportParam.getFlag());
|
||||
createPic(doc, yftj, "月份统计图");
|
||||
two++;
|
||||
}
|
||||
if (exportParam.isTjbg()) {
|
||||
XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
|
||||
createTitle(doc, "4." + i + "." + two + " 时间统计表格", "标题 3", 400, 15);
|
||||
|
||||
XWPFTable table1 = createTable(doc);
|
||||
if (exportParam.getFlag() == 0) {
|
||||
insertRow(doc, table1, centerParagraph, true, "时间(月)", "电压暂降次数");
|
||||
} else {
|
||||
insertRow(doc, table1, centerParagraph, true, "时间(天)", "电压暂降次数");
|
||||
}
|
||||
if (exportParam.getFlag() == 0) {
|
||||
for (int j = 0; j < reasonTypeTime.size(); j++) {
|
||||
TimeVO timeVO = reasonTypeTime.get(j);
|
||||
insertRow(doc, table1, centerParagraph, false, timeVO.getMonth(), timeVO.getTimes());
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; j < reasonTypeTime.size(); j++) {
|
||||
TimeVO timeVO = reasonTypeTime.get(j);
|
||||
insertRow(doc, table1, centerParagraph, false, timeVO.getFulltime(), timeVO.getTimes());
|
||||
}
|
||||
}
|
||||
two++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
//6.原因统计
|
||||
//6.1整合提出查询语句
|
||||
Boolean fly = exportParam.isYybg() || exportParam.isYytx() || exportParam.isLxbg() || exportParam.isLxtx();
|
||||
if (fly) {
|
||||
List<DictData> tempDictType = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.EVENT_STATIS.getCode()).getData();
|
||||
List<String> typeIds = tempDictType.stream().filter(x -> DicDataEnum.VOLTAGE_DIP.getCode().equals(x.getCode()) || DicDataEnum.SHORT_INTERRUPTIONS.getCode().equals(x.getCode()))
|
||||
.map(DictData::getId).collect(Collectors.toList());
|
||||
List<EventDetail> tempInfo = info.stream().filter(temp -> typeIds.contains(temp.getEventType())).collect(Collectors.toList());
|
||||
StatisticVO statistic = eventReportService.getStatistic(tempInfo, reasonData, typeData);
|
||||
if (exportParam.isYybg() || exportParam.isYytx()) {
|
||||
createTitle(doc, "4." + i + " 原因统计", "标题 2", 200, 15);
|
||||
// StatisticVO statistic = eventAnalysisService.getStatistic(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
|
||||
int two = 1;
|
||||
if (exportParam.isYytx()) {
|
||||
createTitle(doc, "4." + i + "." + two + " 原因统计图", "标题 3", 400, 15);
|
||||
List<String> xdata = new ArrayList<>();
|
||||
List<Map<String, Object>> reasonList = new ArrayList<>();
|
||||
|
||||
List<ReasonsVO> reason = statistic.getReason();
|
||||
for (ReasonsVO reasonsVO : reason) {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("value", reasonsVO.getTimes());
|
||||
map.put("name", reasonsVO.getReason());
|
||||
reasonList.add(map);
|
||||
xdata.add(reasonsVO.getReason());
|
||||
}
|
||||
String tr = drawPicUtil.drawReason(xdata, reasonList);
|
||||
createPic(doc, tr, "暂降原因图");
|
||||
two++;
|
||||
}
|
||||
if (exportParam.isYybg()) {
|
||||
XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
|
||||
createTitle(doc, "4." + i + "." + two + " 原因统计表格", "标题 3", 400, 15);
|
||||
XWPFTable table1 = createTable(doc);
|
||||
insertRow(doc, table1, centerParagraph, true, "暂降原因", "电压暂降次数");
|
||||
List<ReasonsVO> reason = statistic.getReason();
|
||||
for (int j = 0; j < reason.size(); j++) {
|
||||
ReasonsVO reasonsVO = reason.get(j);
|
||||
insertRow(doc, table1, centerParagraph, false, reasonsVO.getReason(), reasonsVO.getTimes() + "");
|
||||
}
|
||||
two++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
//7.类型统计
|
||||
if (exportParam.isLxbg() || exportParam.isLxtx()) {
|
||||
createTitle(doc, "4." + i + " 类型统计", "标题 2", 200, 15);
|
||||
// StatisticVO statistic = eventAnalysisService.getStatistic(new StatisticsParam(exportParam.getLineId(), exportParam.getSearchBeginTime(), exportParam.getSearchEndTime(),exportParam.getFlag()));
|
||||
int two = 1;
|
||||
if (exportParam.isLxtx()) {
|
||||
createTitle(doc, "4." + i + "." + two + " 类型统计图", "标题 3", 400, 15);
|
||||
List<String> xdata = new ArrayList<>();
|
||||
List<TypesVO> types = statistic.getTypes();
|
||||
|
||||
List<Map<String, Object>> reasonList = new ArrayList<>();
|
||||
for (TypesVO type : types) {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("value", type.getTimes());
|
||||
map.put("name", type.getType());
|
||||
reasonList.add(map);
|
||||
xdata.add(type.getType());
|
||||
}
|
||||
String tr = drawPicUtil.drawType(xdata, reasonList);
|
||||
createPic(doc, tr, "暂降类型图");
|
||||
two++;
|
||||
}
|
||||
if (exportParam.isLxbg()) {
|
||||
createTitle(doc, "4." + i + "." + two + " 类型统计表格", "标题 3", 400, 15);
|
||||
XWPFParagraph centerParagraph = WordUtils.getCenterParagraph(doc);
|
||||
XWPFTable table1 = createTable(doc);
|
||||
insertRow(doc, table1, centerParagraph, true, "暂降原因", "电压暂降次数");
|
||||
List<TypesVO> types = statistic.getTypes();
|
||||
for (int j = 0; j < types.size(); j++) {
|
||||
TypesVO typesVO = types.get(j);
|
||||
insertRow(doc, table1, centerParagraph, false, typesVO.getType(), typesVO.getTimes() + "");
|
||||
}
|
||||
two++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
String fileName = URLEncoder.encode(exportParam.getLineName() + ".docx", "UTF-8");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
response.setContentType("application/octet-stream;charset=UTF-8");
|
||||
doc.write(outputStream);
|
||||
outputStream.close();
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "导出监测点暂降报告异常");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建标题
|
||||
@@ -598,6 +981,23 @@ public class CommMonitorEventReportServiceImpl implements CommMonitorEventReport
|
||||
return BeanUtil.copyToList(info, EventDetail.class);
|
||||
}
|
||||
|
||||
private List<EventDetail> info(StatisticsParam statisticsParam, Boolean dip) {
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<RmpEventDetailPO> queryWrapper = new LambdaQueryWrapper<RmpEventDetailPO>()
|
||||
.eq(RmpEventDetailPO::getMeasurementPointId, statisticsParam.getLineIndex())
|
||||
.ge(StrUtil.isNotBlank(statisticsParam.getStartTime()), RmpEventDetailPO::getStartTime, DateUtil.beginOfDay(DateUtil.parse(statisticsParam.getStartTime())))
|
||||
.le(StrUtil.isNotBlank(statisticsParam.getEndTime()), RmpEventDetailPO::getStartTime, DateUtil.endOfDay(DateUtil.parse(statisticsParam.getEndTime())))
|
||||
.orderByDesc(RmpEventDetailPO::getStartTime);
|
||||
if (dip) {
|
||||
List<DictData> data = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.EVENT_STATIS.getCode()).getData();
|
||||
List<String> typeList = data.stream().filter(it->it.getCode().equals(DicDataEnum.VOLTAGE_DIP.getCode()) || it.getCode().equals(DicDataEnum.SHORT_INTERRUPTIONS.getCode())).map(DictData::getId).collect(Collectors.toList()); List<TimeVO> list = new ArrayList<>();
|
||||
queryWrapper.in(RmpEventDetailPO::getEventType, typeList);
|
||||
}
|
||||
// 数据暂降查询
|
||||
List<RmpEventDetailPO> info = rmpEventDetailMapper.selectList(queryWrapper);
|
||||
return BeanUtil.copyToList(info, EventDetail.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成暂降事件报告
|
||||
|
||||
@@ -17,4 +17,6 @@ public class AreaHarmReportParam {
|
||||
|
||||
private String deptId;
|
||||
|
||||
private String scale;
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.math.BigDecimal;
|
||||
@@ -132,7 +133,7 @@ public class AreaHarmonicServiceImpl implements AreaHarmonicService {
|
||||
tableList.add(new ArrayList<>());
|
||||
|
||||
// 1. 台账表格
|
||||
List<String[]> ledgerTable = buildLedgerTable(param.getDeptId());
|
||||
List<String[]> ledgerTable = buildLedgerTable(param.getDeptId(),param.getScale());
|
||||
if(CollUtil.isEmpty(ledgerTable)){
|
||||
throw new BusinessException(CommonResponseEnum.FAIL,"当前部门不存在在运监测点");
|
||||
}
|
||||
@@ -145,7 +146,7 @@ public class AreaHarmonicServiceImpl implements AreaHarmonicService {
|
||||
List<OverAreaLimitVO> qualityData = getPowerQualityData(param);
|
||||
if (CollUtil.isNotEmpty(qualityData)) {
|
||||
// 构建监控点名称映射
|
||||
Map<String, String> monitorNameMap = buildMonitorNameMap(param.getDeptId());
|
||||
Map<String, String> monitorNameMap = buildMonitorNameMap(param.getDeptId(),param.getScale());
|
||||
|
||||
// 过滤有效数据(在线监控数>0)
|
||||
List<OverAreaLimitVO> validData = qualityData.stream()
|
||||
@@ -176,8 +177,8 @@ public class AreaHarmonicServiceImpl implements AreaHarmonicService {
|
||||
/**
|
||||
* 构建台账表格
|
||||
*/
|
||||
private List<String[]> buildLedgerTable(String deptId) {
|
||||
List<MonitorCommLedgerInfoDTO> ledgerList = getLedgerInfo(deptId);
|
||||
private List<String[]> buildLedgerTable(String deptId, String scale) {
|
||||
List<MonitorCommLedgerInfoDTO> ledgerList = getLedgerInfo(deptId,scale);
|
||||
if (CollUtil.isEmpty(ledgerList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
@@ -190,7 +191,7 @@ public class AreaHarmonicServiceImpl implements AreaHarmonicService {
|
||||
MonitorCommLedgerInfoDTO ledger = ledgerList.get(i);
|
||||
return new String[]{
|
||||
String.valueOf(i + 1),
|
||||
ledger.getMonitorName(),
|
||||
StringUtils.hasText(ledger.getObjName())? ledger.getObjName()+"_"+ledger.getMonitorName():ledger.getMonitorName(),
|
||||
ledger.getBdName(),
|
||||
ledger.getBusBarName(),
|
||||
voltageLevelMap.getOrDefault(ledger.getVoltageLevel(), ""),
|
||||
@@ -750,10 +751,14 @@ public class AreaHarmonicServiceImpl implements AreaHarmonicService {
|
||||
/**
|
||||
* 获取台账信息
|
||||
*/
|
||||
private List<MonitorCommLedgerInfoDTO> getLedgerInfo(String deptId) {
|
||||
private List<MonitorCommLedgerInfoDTO> getLedgerInfo(String deptId, String scale) {
|
||||
DeptGetLineParam param = new DeptGetLineParam();
|
||||
param.setDeptId(deptId);
|
||||
return commTerminalGeneralClient.deptGetLineInfo(param).getData();
|
||||
List<MonitorCommLedgerInfoDTO> data = commTerminalGeneralClient.deptGetLineInfo(param).getData();
|
||||
if(StringUtils.hasText(scale)){
|
||||
data=data.stream().filter(temp->Objects.equals(scale,temp.getVoltageLevel())).collect(Collectors.toList());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -770,13 +775,14 @@ public class AreaHarmonicServiceImpl implements AreaHarmonicService {
|
||||
/**
|
||||
* 构建监控点名称映射
|
||||
*/
|
||||
private Map<String, String> buildMonitorNameMap(String deptId) {
|
||||
List<MonitorCommLedgerInfoDTO> ledgerList = getLedgerInfo(deptId);
|
||||
private Map<String, String> buildMonitorNameMap(String deptId, String scale) {
|
||||
List<MonitorCommLedgerInfoDTO> ledgerList = getLedgerInfo(deptId, scale);
|
||||
return ledgerList.stream()
|
||||
.collect(Collectors.toMap(
|
||||
MonitorCommLedgerInfoDTO::getMonitorId,
|
||||
MonitorCommLedgerInfoDTO::getMonitorName
|
||||
));
|
||||
MonitorCommLedgerInfoDTO::getMonitorId,
|
||||
temp-> StringUtils.hasText(temp.getObjName())? temp.getObjName()+"_"+temp.getMonitorName():temp.getMonitorName()
|
||||
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
package com.njcn.system.api;
|
||||
|
||||
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.ServerInfo;
|
||||
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.system.api.fallback.CsStatistiacalFeignClientFallbackFactory;
|
||||
import com.njcn.system.pojo.po.CsStatisticalSetPO;
|
||||
import com.njcn.system.pojo.po.EleEpdPqd;
|
||||
import com.njcn.system.pojo.vo.CsStatisticalSetVO;
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
@@ -35,10 +39,12 @@ public interface CsStatisticalSetFeignClient {
|
||||
HttpResult<CsStatisticalSetVO> queryStatistical(@RequestParam("id")String id);
|
||||
|
||||
@PostMapping("/queryStatisticalSelect")
|
||||
HttpResult<List<EleEpdPqd>> queryStatisticalSelect(@RequestParam("id")String id);
|
||||
|
||||
|
||||
@ApiOperation("根据统计类型id组查询已绑定指标")
|
||||
HttpResult<List<EleEpdPqd>> queryStatisticalSelect(@RequestBody List<String> list);
|
||||
|
||||
@PostMapping("/queryStatisticalById")
|
||||
@ApiOperation("根据id查询数据")
|
||||
HttpResult<List<CsStatisticalSetPO>> queryStatisticalById(@RequestBody List<String> list);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,8 @@ package com.njcn.system.api.fallback;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.system.api.AreaFeignClient;
|
||||
import com.njcn.system.api.ConfigFeignClient;
|
||||
import com.njcn.system.api.CsStatisticalSetFeignClient;
|
||||
import com.njcn.system.pojo.dto.AreaTreeDTO;
|
||||
import com.njcn.system.pojo.po.Area;
|
||||
import com.njcn.system.pojo.po.Config;
|
||||
import com.njcn.system.pojo.po.CsStatisticalSetPO;
|
||||
import com.njcn.system.pojo.po.EleEpdPqd;
|
||||
import com.njcn.system.pojo.vo.CsStatisticalSetVO;
|
||||
import com.njcn.system.utils.SystemEnumUtil;
|
||||
@@ -56,9 +52,16 @@ public class CsStatistiacalFeignClientFallbackFactory implements FallbackFactory
|
||||
throw new BusinessException(finalExceptionEnum); }
|
||||
|
||||
@Override
|
||||
public HttpResult<List<EleEpdPqd>> queryStatisticalSelect(String id) {
|
||||
public HttpResult<List<EleEpdPqd>> queryStatisticalSelect(List<String> list) {
|
||||
log.error("{}异常,降级处理,异常为:{}","根据统计类型id查询已绑定指标下拉框",cause.toString());
|
||||
throw new BusinessException(finalExceptionEnum); }
|
||||
throw new BusinessException(finalExceptionEnum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResult<List<CsStatisticalSetPO>> queryStatisticalById(List<String> list) {
|
||||
log.error("{}异常,降级处理,异常为:{}","根据id查询数据异常",cause.toString());
|
||||
throw new BusinessException(finalExceptionEnum);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,11 @@ import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
|
||||
import com.njcn.system.pojo.param.CsStatisticalSetAddParam;
|
||||
import com.njcn.system.pojo.po.CsStatisticalSetPO;
|
||||
import com.njcn.system.pojo.po.EleEpdPqd;
|
||||
import com.njcn.system.pojo.vo.CsStatisticalSetVO;
|
||||
|
||||
import com.njcn.system.service.CsStatisticalSetPOService;
|
||||
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
@@ -64,17 +62,22 @@ public class CsStatisticalSetController extends BaseController {
|
||||
@PostMapping("/queryStatisticalSelect")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("根据统计类型id组查询已绑定指标")
|
||||
public HttpResult<List<EleEpdPqd>> queryStatisticalSelect(@RequestParam("id")String id){
|
||||
public HttpResult<List<EleEpdPqd>> queryStatisticalSelect(@RequestBody List<String> list){
|
||||
log.info("根据模板录入字典数据");
|
||||
String methodDescribe = getMethodDescribe("EleEpdPqd");
|
||||
List<EleEpdPqd> result = csStatisticalSetPOService.queryStatisticalSelect(id);
|
||||
String methodDescribe = getMethodDescribe("queryStatisticalSelect");
|
||||
List<EleEpdPqd> result = csStatisticalSetPOService.queryStatisticalSelect(list);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@PostMapping("/queryStatisticalById")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("根据id查询数据")
|
||||
public HttpResult<List<CsStatisticalSetPO>> queryStatisticalById(@RequestBody List<String> list){
|
||||
log.info("根据模板录入字典数据");
|
||||
String methodDescribe = getMethodDescribe("queryStatisticalById");
|
||||
List<CsStatisticalSetPO> result = csStatisticalSetPOService.queryStatisticalById(list);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -23,5 +23,7 @@ public interface CsStatisticalSetPOService extends IMppService<CsStatisticalSetP
|
||||
|
||||
CsStatisticalSetVO queryStatistical(String id);
|
||||
|
||||
List<EleEpdPqd> queryStatisticalSelect(String id);
|
||||
List<EleEpdPqd> queryStatisticalSelect(List<String> list);
|
||||
|
||||
List<CsStatisticalSetPO> queryStatisticalById(List<String> list);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -108,9 +111,9 @@ public class CsStatisticalSetPOServiceImpl extends MppServiceImpl<CsStatisticalS
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EleEpdPqd> queryStatisticalSelect(String id) {
|
||||
public List<EleEpdPqd> queryStatisticalSelect(List<String> list) {
|
||||
QueryWrapper<CsStatisticalSetPO> queryWrap = new QueryWrapper<>();
|
||||
queryWrap.lambda().eq(CsStatisticalSetPO::getStatisicalId, id);
|
||||
queryWrap.lambda().in(CsStatisticalSetPO::getStatisicalId, list);
|
||||
List<CsStatisticalSetPO> result = this.baseMapper.selectList(queryWrap);
|
||||
List<String> collect = result.stream().map(CsStatisticalSetPO::getTargetId).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(collect)) {
|
||||
@@ -118,6 +121,13 @@ public class CsStatisticalSetPOServiceImpl extends MppServiceImpl<CsStatisticalS
|
||||
}
|
||||
return epdPqdService.listByIds(collect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CsStatisticalSetPO> queryStatisticalById(List<String> list) {
|
||||
QueryWrapper<CsStatisticalSetPO> queryWrap = new QueryWrapper<>();
|
||||
queryWrap.lambda().in(CsStatisticalSetPO::getStatisicalId, list);
|
||||
return this.baseMapper.selectList(queryWrap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.njcn.user.api;
|
||||
|
||||
import com.njcn.common.pojo.constant.ServerInfo;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.user.api.fallback.SmsSendClientFallbackFactory;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* @author xy
|
||||
*/
|
||||
@FeignClient(value = ServerInfo.USER, path = "/sms", fallbackFactory = SmsSendClientFallbackFactory.class,contextId = "sms")
|
||||
public interface SmsSendFeignClient {
|
||||
|
||||
@PostMapping("/send/simple")
|
||||
@ApiOperation("发送短信(简化参数)")
|
||||
HttpResult<String> sendSmsSimple(@RequestParam("receiver") String receiver
|
||||
, @RequestParam("content") String content
|
||||
, @RequestParam("messageType") String messageType);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.njcn.user.api.fallback;
|
||||
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.user.api.SmsSendFeignClient;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author xy
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SmsSendClientFallbackFactory implements FallbackFactory<SmsSendFeignClient> {
|
||||
@Override
|
||||
public SmsSendFeignClient create(Throwable cause) {
|
||||
//判断抛出异常是否为解码器抛出的业务异常
|
||||
Enum<?> exceptionEnum = CommonResponseEnum.SERVICE_FALLBACK;
|
||||
if (cause.getCause() instanceof BusinessException) {
|
||||
BusinessException businessException = (BusinessException) cause.getCause();
|
||||
}
|
||||
Enum<?> finalExceptionEnum = exceptionEnum;
|
||||
return new SmsSendFeignClient() {
|
||||
|
||||
@Override
|
||||
public HttpResult<String> sendSmsSimple(String receiver, String content, String messageType) {
|
||||
log.error("{}异常,降级处理,异常为:{}","发送短信(简化参数)数据异常",cause.toString());
|
||||
throw new BusinessException(finalExceptionEnum);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.njcn.user.pojo.dto;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2026-03-31
|
||||
*/
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 系统凭证请求 DTO
|
||||
*
|
||||
* @author msgpush
|
||||
*/
|
||||
@Data
|
||||
public class CredentialReqDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 上游系统名称
|
||||
*/
|
||||
@NotEmpty(message = "上游系统名称不能为空")
|
||||
private String systemName;
|
||||
|
||||
/**
|
||||
* 密钥(用于生成凭证)
|
||||
*/
|
||||
@NotEmpty(message = "密钥不能为空")
|
||||
private String secretKey;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.user.pojo.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author xy
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class SendResult implements Serializable {
|
||||
|
||||
private final boolean success;
|
||||
private final String messageId;
|
||||
private final String failReason;
|
||||
private final boolean isTimeOut;
|
||||
private final boolean unauthorized;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.njcn.user.pojo.po.app;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author xy
|
||||
*/
|
||||
@Data
|
||||
@TableName("sms_send_record")
|
||||
public class SmsSendRecord implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
private String receiver;
|
||||
|
||||
private String content;
|
||||
|
||||
private String messageType;
|
||||
|
||||
private String credentialToken;
|
||||
|
||||
private Integer sendStatus;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.IGNORED)
|
||||
private String failReason;
|
||||
|
||||
private Integer retryCount;
|
||||
|
||||
private Integer maxRetry;
|
||||
|
||||
private Long responseTime;
|
||||
|
||||
private LocalDateTime sendTime;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.user.pojo.vo.app;
|
||||
|
||||
import cn.hutool.core.lang.RegexPool;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
@Data
|
||||
@Schema(description = "管理后台 - 消息记录发送 Request VO")
|
||||
public class MessageRecordReqVO {
|
||||
|
||||
private String channel;
|
||||
|
||||
@Schema(description = "消息类型", example = "verify_code/order_notify/marketing/system_notify")
|
||||
@NotBlank(message = "消息类型不能为空")
|
||||
private String messageType;
|
||||
|
||||
@Schema(description = "接收者")
|
||||
@NotBlank(message = "接收者不能为空")
|
||||
@Pattern(regexp = RegexPool.EMAIL + "|" + RegexPool.MOBILE, message = "必须是有效的邮箱或手机号格式")
|
||||
private String receiver;
|
||||
|
||||
@Schema(description = "标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "消息内容")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "模板编码")
|
||||
private String templateCode;
|
||||
|
||||
@Schema(description = "模板参数")
|
||||
private String templateParams;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.njcn.user.controller.message;
|
||||
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.user.pojo.vo.app.MessageRecordReqVO;
|
||||
import com.njcn.user.service.ISmsSendService;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* @author xy
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/sms")
|
||||
@Api(tags = "短信发送管理")
|
||||
@AllArgsConstructor
|
||||
public class SmsSendController extends BaseController {
|
||||
|
||||
private final ISmsSendService smsSendService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/send")
|
||||
@ApiOperation("发送短信(同步,包含重试)")
|
||||
public HttpResult<String> sendSms(@RequestBody MessageRecordReqVO vo) {
|
||||
String methodDescribe = getMethodDescribe("sendSms");
|
||||
|
||||
try {
|
||||
smsSendService.sendSmsWithRetry(vo);
|
||||
return HttpResultUtil.assembleCommonResponseResult(
|
||||
CommonResponseEnum.SUCCESS,
|
||||
"短信发送成功",
|
||||
methodDescribe
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("短信发送失败", e);
|
||||
return HttpResultUtil.assembleCommonResponseResult(
|
||||
CommonResponseEnum.FAIL,
|
||||
e.getMessage(),
|
||||
methodDescribe
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/send/simple")
|
||||
@ApiOperation("发送短信(简化参数)")
|
||||
public HttpResult<String> sendSmsSimple(
|
||||
@RequestParam String receiver,
|
||||
@RequestParam String content,
|
||||
@RequestParam(defaultValue = "verify_code") String messageType) {
|
||||
String methodDescribe = getMethodDescribe("sendSmsSimple");
|
||||
|
||||
try {
|
||||
smsSendService.sendSmsWithRetry(receiver, content, messageType);
|
||||
return HttpResultUtil.assembleCommonResponseResult(
|
||||
CommonResponseEnum.SUCCESS,
|
||||
"短信发送成功",
|
||||
methodDescribe
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("短信发送失败", e);
|
||||
return HttpResultUtil.assembleCommonResponseResult(
|
||||
CommonResponseEnum.FAIL,
|
||||
e.getMessage(),
|
||||
methodDescribe
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.njcn.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.njcn.user.pojo.po.app.SmsSendRecord;
|
||||
|
||||
public interface CsSmsSendRecordMapper extends BaseMapper<SmsSendRecord> {
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
package com.njcn.user.mapper;
|
||||
|
||||
import com.alibaba.nacos.shaded.com.google.gson.Gson;
|
||||
import com.alibaba.nacos.shaded.com.google.gson.JsonObject;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.redis.utils.RedisUtil;
|
||||
import com.njcn.user.pojo.dto.CredentialReqDTO;
|
||||
import com.njcn.user.pojo.dto.SendResult;
|
||||
import com.njcn.user.pojo.po.app.SmsSendRecord;
|
||||
import com.njcn.user.pojo.vo.app.MessageRecordReqVO;
|
||||
import com.njcn.user.service.ISmsSendService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author xy
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public class SmsSendServiceImpl extends ServiceImpl<CsSmsSendRecordMapper, SmsSendRecord> implements ISmsSendService {
|
||||
|
||||
@Value("${msg.credential_url:http://192.168.2.126:48083/admin-api/push/credential/generate}")
|
||||
private String CREDENTIAL_URL;
|
||||
@Value("${msg.sms_send_url:http://192.168.2.126:48083/admin-api/push/message/send/sms}")
|
||||
private String SMS_SEND_URL;
|
||||
@Value("${msg.connect_timeout:5000}")
|
||||
private Integer CONNECT_TIMEOUT;
|
||||
@Value("${msg.read_timeout:30000}")
|
||||
private Integer READ_TIMEOUT;
|
||||
@Value("${msg.system_name:NPQS-9500}")
|
||||
private String SYSTEM_NAME;
|
||||
@Value("${msg.secret_key:123456}")
|
||||
private String SECRET_KEY;
|
||||
|
||||
private static final int[] RETRY_DELAYS = {1, 2, 3};
|
||||
private static final int MAX_RETRY = 3;
|
||||
private static final String CREDENTIAL_CACHE_KEY = "SMS_CREDENTIAL_TOKEN";
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Override
|
||||
public void sendSmsWithRetry(String receiver, String content, String messageType) {
|
||||
MessageRecordReqVO vo = new MessageRecordReqVO();
|
||||
vo.setReceiver(receiver);
|
||||
vo.setContent(content);
|
||||
vo.setMessageType(messageType);
|
||||
sendSmsWithRetry(vo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSmsWithRetry(MessageRecordReqVO messageRecordReqVO) {
|
||||
SmsSendRecord record = initRecord(messageRecordReqVO);
|
||||
|
||||
this.save(record);
|
||||
|
||||
try {
|
||||
String credentialToken = getOrRefreshCredentialWithRetry(record);
|
||||
|
||||
if (credentialToken == null) {
|
||||
record.setSendStatus(0);
|
||||
record.setFailReason("获取凭证失败,已重试3次");
|
||||
log.error("获取凭证失败,短信未发送,接收者: {}", messageRecordReqVO.getReceiver());
|
||||
this.updateById(record);
|
||||
throw new BusinessException("获取凭证失败,已重试3次");
|
||||
}
|
||||
|
||||
record.setCredentialToken(credentialToken);
|
||||
record.setSendTime(LocalDateTime.now());
|
||||
this.updateById(record);
|
||||
|
||||
boolean success = attemptSendWithRetry(messageRecordReqVO, credentialToken, record);
|
||||
|
||||
if (success) {
|
||||
record.setSendStatus(1);
|
||||
record.setFailReason(null);
|
||||
log.info("短信发送成功,接收者: {}", messageRecordReqVO.getReceiver());
|
||||
} else {
|
||||
record.setSendStatus(0);
|
||||
if (record.getFailReason() == null) {
|
||||
record.setFailReason("超过最大重试次数,发送失败");
|
||||
}
|
||||
log.error("短信发送失败,接收者: {},已重试{}次,原因: {}",
|
||||
messageRecordReqVO.getReceiver(), record.getRetryCount(), record.getFailReason());
|
||||
throw new BusinessException("短信发送失败: " + record.getFailReason());
|
||||
}
|
||||
} catch (BusinessException e) {
|
||||
record.setSendStatus(0);
|
||||
record.setFailReason(e.getMessage());
|
||||
log.error("短信发送业务异常,接收者: {}", messageRecordReqVO.getReceiver(), e);
|
||||
this.updateById(record);
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
record.setSendStatus(0);
|
||||
record.setFailReason("发送异常: " + e.getMessage());
|
||||
log.error("短信发送异常,接收者: {}", messageRecordReqVO.getReceiver(), e);
|
||||
this.updateById(record);
|
||||
throw new BusinessException("短信发送异常: " + e.getMessage());
|
||||
} finally {
|
||||
this.updateById(record);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsSendRecord initRecord(MessageRecordReqVO vo) {
|
||||
SmsSendRecord record = new SmsSendRecord();
|
||||
record.setReceiver(vo.getReceiver());
|
||||
record.setContent(vo.getContent());
|
||||
record.setMessageType(vo.getMessageType());
|
||||
record.setSendStatus(-1);
|
||||
record.setRetryCount(0);
|
||||
record.setMaxRetry(MAX_RETRY);
|
||||
record.setCreateTime(LocalDateTime.now());
|
||||
return record;
|
||||
}
|
||||
|
||||
private String getOrRefreshCredentialWithRetry(SmsSendRecord record) {
|
||||
Object cachedToken = redisUtil.getObjectByKey(CREDENTIAL_CACHE_KEY);
|
||||
if (cachedToken != null) {
|
||||
log.info("使用缓存的凭证令牌");
|
||||
return cachedToken.toString();
|
||||
}
|
||||
|
||||
log.info("缓存中无凭证,开始获取新凭证(最多重试3次)");
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
try {
|
||||
String token = fetchNewCredential();
|
||||
log.info("第{}次尝试获取凭证成功", i);
|
||||
return token;
|
||||
} catch (Exception e) {
|
||||
log.warn("第{}次获取凭证失败: {}", i, e.getMessage());
|
||||
|
||||
record.setFailReason("获取凭证第" + i + "次失败: " + e.getMessage());
|
||||
this.updateById(record);
|
||||
|
||||
try {
|
||||
int waitSeconds = i * 10;
|
||||
log.info("等待{}秒后重试...", waitSeconds);
|
||||
TimeUnit.SECONDS.sleep(waitSeconds);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("凭证获取重试被中断");
|
||||
record.setFailReason("获取凭证实例被中断");
|
||||
this.updateById(record);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.error("获取凭证失败,已重试3次");
|
||||
return null;
|
||||
}
|
||||
|
||||
private String fetchNewCredential() {
|
||||
CredentialReqDTO reqDTO = new CredentialReqDTO();
|
||||
reqDTO.setSystemName(SYSTEM_NAME);
|
||||
reqDTO.setSecretKey(SECRET_KEY);
|
||||
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
URL url = new URL(CREDENTIAL_URL);
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setConnectTimeout(CONNECT_TIMEOUT);
|
||||
connection.setReadTimeout(READ_TIMEOUT);
|
||||
connection.setDoOutput(true);
|
||||
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(GSON.toJson(reqDTO).getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode != 200) {
|
||||
throw new BusinessException("获取凭证失败,HTTP响应码: " + responseCode);
|
||||
}
|
||||
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
|
||||
StringBuilder response = new StringBuilder();
|
||||
String inputLine;
|
||||
while ((inputLine = reader.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
}
|
||||
reader.close();
|
||||
|
||||
JsonObject jsonResponse = GSON.fromJson(response.toString(), JsonObject.class);
|
||||
int code = jsonResponse.get("code").getAsInt();
|
||||
|
||||
if (code != 0) {
|
||||
String msg = jsonResponse.has("msg") ? jsonResponse.get("msg").getAsString() : "未知错误";
|
||||
throw new BusinessException("获取凭证失败,错误码: " + code + ",错误信息: " + msg);
|
||||
}
|
||||
|
||||
JsonObject data = jsonResponse.getAsJsonObject("data");
|
||||
String token = data.get("credentialToken").getAsString();
|
||||
long expiresTimestamp = data.get("expiresTime").getAsLong();
|
||||
|
||||
LocalDateTime expiresTime = LocalDateTime.ofInstant(
|
||||
Instant.ofEpochMilli(expiresTimestamp),
|
||||
ZoneId.systemDefault()
|
||||
);
|
||||
|
||||
long expireSeconds = calculateExpireSeconds(expiresTime);
|
||||
redisUtil.saveByKeyWithExpire(CREDENTIAL_CACHE_KEY, token, expireSeconds);
|
||||
|
||||
log.info("获取新凭证成功,过期时间: {},缓存有效期: {}秒", expiresTime, expireSeconds);
|
||||
return token;
|
||||
|
||||
} catch (SocketTimeoutException e) {
|
||||
throw new BusinessException("获取凭证超时(30秒),请检查网络连接");
|
||||
} catch (ConnectException e) {
|
||||
throw new BusinessException("无法连接到凭证服务,请检查服务是否启动和网络是否正常");
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException("获取凭证IO异常: " + e.getMessage());
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long calculateExpireSeconds(LocalDateTime expiresTime) {
|
||||
long expireSeconds = java.time.Duration.between(
|
||||
LocalDateTime.now(),
|
||||
expiresTime
|
||||
).getSeconds();
|
||||
|
||||
expireSeconds = expireSeconds - 60;
|
||||
|
||||
return Math.max(expireSeconds, 60);
|
||||
}
|
||||
|
||||
private boolean attemptSendWithRetry(MessageRecordReqVO vo, String token, SmsSendRecord record) {
|
||||
for (int attempt = 0; attempt <= MAX_RETRY; attempt++) {
|
||||
if (attempt > 0) {
|
||||
int delayMinutes = RETRY_DELAYS[attempt - 1];
|
||||
log.info("第{}次重试,等待{}分钟后发送...", attempt, delayMinutes);
|
||||
|
||||
try {
|
||||
TimeUnit.MINUTES.sleep(delayMinutes);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("重试等待被中断", e);
|
||||
record.setFailReason("重试等待被中断");
|
||||
return false;
|
||||
}
|
||||
record.setRetryCount(attempt);
|
||||
}
|
||||
|
||||
SendResult result = executeSendSms(vo, token, record);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
record.setFailReason(null);
|
||||
log.info("第{}次尝试发送成功,消息ID: {}", attempt, result.getMessageId());
|
||||
return true;
|
||||
}
|
||||
|
||||
record.setFailReason(result.getFailReason());
|
||||
|
||||
if (result.isUnauthorized()) {
|
||||
log.warn("凭证失效(401),重新获取凭证后重试...");
|
||||
String newToken = getOrRefreshCredentialWithRetry(record);
|
||||
if (newToken == null) {
|
||||
record.setFailReason("凭证刷新失败,无法重新获取凭证");
|
||||
return false;
|
||||
}
|
||||
token = newToken;
|
||||
record.setCredentialToken(newToken);
|
||||
this.updateById(record);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!result.isTimeOut()) {
|
||||
log.warn("发送失败且非超时,不再重试,原因: {},响应时间: {}ms",
|
||||
result.getFailReason(), record.getResponseTime());
|
||||
return false;
|
||||
}
|
||||
|
||||
log.warn("第{}次发送超时,将重试,响应时间: {}ms",
|
||||
attempt, record.getResponseTime());
|
||||
}
|
||||
|
||||
record.setFailReason("超过最大重试次数,发送超时");
|
||||
return false;
|
||||
}
|
||||
|
||||
private SendResult executeSendSms(MessageRecordReqVO vo, String token, SmsSendRecord record) {
|
||||
HttpURLConnection connection = null;
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
URL url = new URL(SMS_SEND_URL);
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("X-Credential-Token", token);
|
||||
connection.setConnectTimeout(CONNECT_TIMEOUT);
|
||||
connection.setReadTimeout(READ_TIMEOUT);
|
||||
connection.setDoOutput(true);
|
||||
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(GSON.toJson(Collections.singletonList(vo)).getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
long responseTime = System.currentTimeMillis() - startTime;
|
||||
record.setResponseTime(responseTime);
|
||||
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
responseCode == 200 ? connection.getInputStream() : connection.getErrorStream(),
|
||||
StandardCharsets.UTF_8
|
||||
)
|
||||
);
|
||||
StringBuilder response = new StringBuilder();
|
||||
String inputLine;
|
||||
while ((inputLine = reader.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
}
|
||||
reader.close();
|
||||
|
||||
if (responseCode != 200) {
|
||||
String failReason = "HTTP响应码异常: " + responseCode;
|
||||
if (response.length() > 0) {
|
||||
failReason += ",响应: " + response.toString();
|
||||
}
|
||||
record.setFailReason(failReason);
|
||||
return new SendResult(false, null, failReason, false, false);
|
||||
}
|
||||
|
||||
JsonObject jsonResponse = GSON.fromJson(response.toString(), JsonObject.class);
|
||||
int code = jsonResponse.get("code").getAsInt();
|
||||
|
||||
if (code == 401) {
|
||||
String failReason = "凭证失效(HTTP 401)";
|
||||
record.setFailReason(failReason);
|
||||
redisUtil.delete(CREDENTIAL_CACHE_KEY);
|
||||
return new SendResult(false, null, failReason, false, true);
|
||||
} else {
|
||||
if (code != 0) {
|
||||
String msg = jsonResponse.has("msg") ? jsonResponse.get("msg").getAsString() : "未知错误";
|
||||
String failReason = "业务错误码: " + code + ",错误信息: " + msg;
|
||||
record.setFailReason(failReason);
|
||||
return new SendResult(false, null, failReason, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject firstResult = jsonResponse.getAsJsonArray("data").get(0).getAsJsonObject();
|
||||
boolean result = firstResult.get("result").getAsBoolean();
|
||||
String messageId = firstResult.has("messageId") ? firstResult.get("messageId").getAsString() : null;
|
||||
String detail = firstResult.has("detail") ? firstResult.get("detail").getAsString() : null;
|
||||
|
||||
if (result) {
|
||||
log.info("短信发送成功,接收者: {},消息ID: {},详情: {},耗时: {}ms",
|
||||
vo.getReceiver(), messageId, detail, responseTime);
|
||||
return new SendResult(true, messageId, null, false, false);
|
||||
} else {
|
||||
String failReason = "发送失败: " + detail;
|
||||
record.setFailReason(failReason);
|
||||
return new SendResult(false, messageId, failReason, false, false);
|
||||
}
|
||||
|
||||
} catch (SocketTimeoutException e) {
|
||||
long responseTime = System.currentTimeMillis() - startTime;
|
||||
record.setResponseTime(responseTime);
|
||||
String failReason = "请求超时(30秒)";
|
||||
record.setFailReason(failReason);
|
||||
log.warn("短信发送超时,接收者: {},耗时: {}ms", vo.getReceiver(), responseTime);
|
||||
return new SendResult(false, null, failReason, true, false);
|
||||
} catch (ConnectException e) {
|
||||
long responseTime = System.currentTimeMillis() - startTime;
|
||||
record.setResponseTime(responseTime);
|
||||
String failReason = "无法连接到短信服务";
|
||||
record.setFailReason(failReason);
|
||||
log.error("短信服务连接失败,接收者: {}", vo.getReceiver(), e);
|
||||
return new SendResult(false, null, failReason, false, false);
|
||||
} catch (IOException e) {
|
||||
long responseTime = System.currentTimeMillis() - startTime;
|
||||
record.setResponseTime(responseTime);
|
||||
String failReason = "IO异常: " + e.getMessage();
|
||||
record.setFailReason(failReason);
|
||||
log.error("短信发送IO异常,接收者: {},耗时: {}ms", vo.getReceiver(), responseTime, e);
|
||||
return new SendResult(false, null, failReason, false, false);
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.njcn.user.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.user.pojo.po.app.SmsSendRecord;
|
||||
import com.njcn.user.pojo.vo.app.MessageRecordReqVO;
|
||||
|
||||
public interface ISmsSendService extends IService<SmsSendRecord> {
|
||||
|
||||
void sendSmsWithRetry(String receiver, String content, String messageType);
|
||||
|
||||
void sendSmsWithRetry(MessageRecordReqVO messageRecordReqVO);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.njcn.user.service.impl;
|
||||
|
||||
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
@@ -19,7 +18,6 @@ import com.njcn.user.pojo.po.Role;
|
||||
import com.njcn.user.pojo.po.User;
|
||||
import com.njcn.user.pojo.po.UserSet;
|
||||
import com.njcn.user.pojo.po.app.AppInfoSet;
|
||||
import com.njcn.user.pojo.po.app.AppSendMsg;
|
||||
import com.njcn.user.service.*;
|
||||
import com.njcn.user.util.SmsApiUtil;
|
||||
import com.njcn.user.util.SmsUtil;
|
||||
@@ -46,22 +44,14 @@ import java.util.*;
|
||||
public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, User> implements IAppUserService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AppUserServiceImpl.class);
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
private final IAppSendMsgService appSendMsgService;
|
||||
|
||||
private final IUserSetService userSetService;
|
||||
|
||||
private final IRoleService roleService;
|
||||
|
||||
private final IUserRoleService userRoleService;
|
||||
|
||||
private final IAppInfoSetService appInfoSetService;
|
||||
|
||||
private final SmsUtil smsUtil;
|
||||
|
||||
private final SmsApiUtil smsApiUtil;
|
||||
private final ISmsSendService smsSendService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -69,8 +59,6 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, User> impleme
|
||||
if (!PubUtils.match(PatternRegex.PHONE_REGEX, phone)){
|
||||
throw new BusinessException(UserResponseEnum.REGISTER_PHONE_WRONG);
|
||||
}
|
||||
SendSmsResponse sendSmsResponse = null;
|
||||
ResponseEntity<String> response = null;
|
||||
String msgTemplate = SmsUtil.getMessageTemplate(type);
|
||||
String vcode = null;
|
||||
try {
|
||||
@@ -97,70 +85,17 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, User> impleme
|
||||
//开始执行短信发送
|
||||
String mobiles = phone;
|
||||
String content = SmsUtil.getLianTongMessageTemplate(type, vcode);
|
||||
response = smsApiUtil.sendBatchSms(mobiles, content);
|
||||
smsSendService.sendSmsWithRetry(mobiles,content,"verify_code");
|
||||
String key = RedisKeyEnum.SMS_LOGIN_KEY.getKey() + phone;
|
||||
//成功发送短信验证码后,保存进redis
|
||||
redisUtil.saveByKeyWithExpire(key, vcode, 300L);
|
||||
//短信入库
|
||||
addZdSendMessage(phone,vcode,msgTemplate,response);
|
||||
} catch (Exception e) {
|
||||
logger.error("发送短信异常,异常为:"+e.getMessage());
|
||||
//短信入库
|
||||
addZdSendMessage(phone,vcode,msgTemplate,response);
|
||||
throw new BusinessException(e.getMessage());
|
||||
}
|
||||
return "OK";
|
||||
}
|
||||
|
||||
// @Override
|
||||
// @Transactional(rollbackFor = Exception.class)
|
||||
// public String setMessage(String phone, String devCode, String type) {
|
||||
// if (!PubUtils.match(PatternRegex.PHONE_REGEX, phone)){
|
||||
// throw new BusinessException(UserResponseEnum.REGISTER_PHONE_WRONG);
|
||||
// }
|
||||
// SendSmsResponse sendSmsResponse = null;
|
||||
// String msgTemplate = SmsUtil.getMessageTemplate(type);
|
||||
// String vcode = null;
|
||||
// try {
|
||||
// //type为4,账号替换为新手机号
|
||||
// if (!msgTemplate.equalsIgnoreCase(MessageEnum.REGISTER.getTemplateCode())) {
|
||||
// User user = this.lambdaQuery().eq(User::getPhone,phone).one();
|
||||
// if ("4".equalsIgnoreCase(type)) {
|
||||
// //注册,无需判断手机号与设备的匹配
|
||||
// if (user != null) {
|
||||
// throw new BusinessException(UserResponseEnum.REGISTER_PHONE_FAIL);
|
||||
// }
|
||||
// } else {
|
||||
// if (null == user) {
|
||||
// throw new BusinessException(UserResponseEnum.LOGIN_PHONE_NOT_REGISTER);
|
||||
// } else {
|
||||
// user.setDevCode(devCode);
|
||||
// logger.info("更新手机id:" + devCode);
|
||||
// this.updateById(user);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// vcode = getMessageCode();
|
||||
// //获取短信发送结果
|
||||
// sendSmsResponse = smsUtil.sendSms(phone,msgTemplate,"code",vcode);
|
||||
// String key = RedisKeyEnum.SMS_LOGIN_KEY.getKey() + phone;
|
||||
// if (sendSmsResponse.getCode() != null && "OK".equals(sendSmsResponse.getCode())) {
|
||||
// //成功发送短信验证码后,保存进redis,验证码失效为5分钟
|
||||
// redisUtil.saveByKeyWithExpire(key, vcode,300L);
|
||||
// } else {
|
||||
// throw new BusinessException(UserResponseEnum.SEND_CODE_FAIL);
|
||||
// }
|
||||
// //短信入库
|
||||
// addSendMessage(phone,vcode,msgTemplate,sendSmsResponse);
|
||||
// } catch (Exception e) {
|
||||
// logger.error("发送短信异常,异常为:"+e.getMessage());
|
||||
// //短信入库
|
||||
// addSendMessage(phone,vcode,msgTemplate,sendSmsResponse);
|
||||
// throw new BusinessException(e.getMessage());
|
||||
// }
|
||||
// return sendSmsResponse.getCode();
|
||||
// }
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = {Exception.class})
|
||||
public void register(String phone, String code, String devCode) {
|
||||
@@ -172,7 +107,6 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, User> impleme
|
||||
}
|
||||
judgeCode(phone, code);
|
||||
String password = null;
|
||||
ResponseEntity<String> response = null;
|
||||
//先根据手机号查询是否已被注册
|
||||
User user = this.lambdaQuery().eq(User::getPhone,phone).ne(User::getState,0).one();
|
||||
if (!Objects.isNull(user)){
|
||||
@@ -198,73 +132,13 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, User> impleme
|
||||
//发送用户初始密码
|
||||
password = redisUtil.getStringByKey(newUser.getId());
|
||||
String content = SmsUtil.getLianTongMessageTemplate("3", password);
|
||||
response = smsApiUtil.sendBatchSms(phone, content);
|
||||
if (response != null && response.getStatusCodeValue() == 200) {
|
||||
//成功发送短信验证码后,删除用户密码信息
|
||||
redisUtil.delete(newUser.getId());
|
||||
} else {
|
||||
throw new BusinessException(UserResponseEnum.SEND_CODE_FAIL);
|
||||
}
|
||||
addZdSendMessage(phone,password,MessageEnum.getTemplateByCode(3),response);
|
||||
smsSendService.sendSmsWithRetry(phone,content,"verify_code");
|
||||
redisUtil.delete(newUser.getId());
|
||||
//删除验证码
|
||||
deleteCode(phone);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// @Transactional(rollbackFor = {Exception.class})
|
||||
// public void register(String phone, String code, String devCode) {
|
||||
// if (!PubUtils.match(PatternRegex.PHONE_REGEX, phone)){
|
||||
// throw new BusinessException(UserResponseEnum.REGISTER_PHONE_WRONG);
|
||||
// }
|
||||
// if (StringUtils.isBlank(devCode)) {
|
||||
// throw new BusinessException(UserResponseEnum.DEV_CODE_WRONG);
|
||||
// }
|
||||
// judgeCode(phone, code);
|
||||
// String password = null;
|
||||
// SendSmsResponse sendSmsResponse = null;
|
||||
// //先根据手机号查询是否已被注册
|
||||
// User user = this.lambdaQuery().eq(User::getPhone,phone).ne(User::getState,0).one();
|
||||
// if (!Objects.isNull(user)){
|
||||
// throw new BusinessException(UserResponseEnum.REGISTER_PHONE_REPEAT);
|
||||
// } else {
|
||||
// //新增用户配置表
|
||||
// UserSet userSet = userSetService.addAppUserSet();
|
||||
// //新增用户表
|
||||
// User newUser = cloneUserBoToUser(phone,devCode,userSet);
|
||||
// //新增用户角色关系表
|
||||
// Role role = roleService.getRoleByCode(AppRoleEnum.TOURIST.getCode());
|
||||
// userRoleService.addUserRole(newUser.getId(), Collections.singletonList(role.getId()));
|
||||
// //消息默认配置
|
||||
// AppInfoSet appInfoSet = new AppInfoSet();
|
||||
// appInfoSet.setUserId(newUser.getId());
|
||||
// appInfoSet.setHarmonicInfo(1);
|
||||
// appInfoSet.setEventInfo(1);
|
||||
// appInfoSet.setRunInfo(1);
|
||||
// appInfoSet.setAlarmInfo(1);
|
||||
// appInfoSet.setFunctionBug(0);
|
||||
// appInfoSet.setExFactoryBug(0);
|
||||
// appInfoSetService.save(appInfoSet);
|
||||
// //发送用户初始密码
|
||||
// try {
|
||||
// password = redisUtil.getStringByKey(newUser.getId());
|
||||
// sendSmsResponse = smsUtil.sendSms(phone,MessageEnum.getTemplateByCode(3),"pwd",password);
|
||||
// if (sendSmsResponse.getCode() != null && "OK".equals(sendSmsResponse.getCode())) {
|
||||
// //成功发送短信验证码后,删除用户密码信息
|
||||
// redisUtil.delete(newUser.getId());
|
||||
// } else {
|
||||
// throw new BusinessException(UserResponseEnum.SEND_CODE_FAIL);
|
||||
// }
|
||||
// addSendMessage(phone,password,MessageEnum.getTemplateByCode(3),sendSmsResponse);
|
||||
// } catch (ClientException e) {
|
||||
// logger.error("发送短信异常,异常为:"+e.getMessage());
|
||||
// addSendMessage(phone,password,MessageEnum.getTemplateByCode(3),sendSmsResponse);
|
||||
// }
|
||||
// //删除验证码
|
||||
// deleteCode(phone);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void modifyPsd(String userId, String phone, String code, String password, String devCode) {
|
||||
if (!PubUtils.match(PatternRegex.PHONE_REGEX, phone)){
|
||||
@@ -426,41 +300,4 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, User> impleme
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码入库
|
||||
*/
|
||||
public void addSendMessage(String phone, String vcode, String template, SendSmsResponse sendSmsResponse) {
|
||||
AppSendMsg appSendMsg = new AppSendMsg();
|
||||
appSendMsg.setPhone(phone);
|
||||
appSendMsg.setMessage(vcode);
|
||||
appSendMsg.setSendTime(LocalDateTime.now());
|
||||
if (Objects.isNull(sendSmsResponse)){
|
||||
appSendMsg.setSendStatus("无状态");
|
||||
appSendMsg.setRemark(null);
|
||||
} else {
|
||||
appSendMsg.setSendStatus(sendSmsResponse.getCode() == null ? "无状态" : sendSmsResponse.getCode());
|
||||
appSendMsg.setRemark(sendSmsResponse.getMessage());
|
||||
}
|
||||
appSendMsg.setTemplate(template);
|
||||
appSendMsgService.save(appSendMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 中国电信验证码入库
|
||||
*/
|
||||
public void addZdSendMessage(String phone, String vcode, String template, ResponseEntity<String> response) {
|
||||
AppSendMsg appSendMsg = new AppSendMsg();
|
||||
appSendMsg.setPhone(phone);
|
||||
appSendMsg.setMessage(vcode);
|
||||
appSendMsg.setSendTime(LocalDateTime.now());
|
||||
if (Objects.isNull(response)){
|
||||
appSendMsg.setSendStatus("无状态");
|
||||
appSendMsg.setRemark(null);
|
||||
} else {
|
||||
appSendMsg.setSendStatus(String.valueOf(response.getStatusCodeValue()));
|
||||
}
|
||||
appSendMsg.setTemplate(template);
|
||||
appSendMsgService.save(appSendMsg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -480,11 +480,21 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
|
||||
@Override
|
||||
public List<User> getMarketList() {
|
||||
Role roleByCode = roleService.getRoleByCode(AppRoleEnum.MARKET_USER.getCode());
|
||||
List<UserRole> userRoles = userRoleMapper.selectUserRole(Stream.of(roleByCode.getId()).collect(Collectors.toList()));
|
||||
List<String> collect = userRoles.stream().map(UserRole::getUserId).collect(Collectors.toList());
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.ne(User::getState, UserState.DELETE).in(User::getId, collect);
|
||||
return this.list(queryWrapper);
|
||||
List<UserRole> userRoles = userRoleMapper.selectUserRole(
|
||||
Collections.singletonList(roleByCode.getId())
|
||||
);
|
||||
List<String> userIds = userRoles.stream()
|
||||
.map(UserRole::getUserId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (userIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return this.lambdaQuery()
|
||||
.ne(User::getState, UserState.DELETE)
|
||||
.in(User::getId, userIds)
|
||||
.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user