feat(report): 优化报告生成工具中的文档处理功能
- 在Docx4jUtil中添加enableUpdateFieldsOnOpen方法,确保Word打开时自动刷新页码等域 - 移除ItemAnchorAssembler中不必要的导入包,精简代码结构 - 重构谐波数据处理逻辑,添加谐波衍生值计算功能 - 实现固定占位符替换机制,支持谐波电压和功率表的动态填充 - 优化表格结构分离逻辑,使用空段落而非可见标题分隔多表 - 增强错误范围处理,移除单位后缀并优化谐波电流计算逻辑 - 添加全面的单元测试验证各种谐波检测场景的功能正确性
This commit is contained in:
@@ -58,7 +58,21 @@ public enum ItemReportKeyEnum {
|
|||||||
POWER_FACTOR("powerFactor", "P表:功率因数实测值(数据侧暂无,留空)"),
|
POWER_FACTOR("powerFactor", "P表:功率因数实测值(数据侧暂无,留空)"),
|
||||||
POS_SEQ("posSeq", "不平衡表:基波正序(数据侧暂无,留空)"),
|
POS_SEQ("posSeq", "不平衡表:基波正序(数据侧暂无,留空)"),
|
||||||
NEG_SEQ("negSeq", "不平衡表:基波负序(数据侧暂无,留空)"),
|
NEG_SEQ("negSeq", "不平衡表:基波负序(数据侧暂无,留空)"),
|
||||||
ZERO_SEQ("zeroSeq", "不平衡表:基波零序(数据侧暂无,留空)");
|
ZERO_SEQ("zeroSeq", "不平衡表:基波零序(数据侧暂无,留空)"),
|
||||||
|
BASE_CURRENT_A("baseCurrentA", "HI表:A相基波电流幅值,用于计算谐波电流含有率"),
|
||||||
|
BASE_CURRENT_B("baseCurrentB", "HI表:B相基波电流幅值,用于计算谐波电流含有率"),
|
||||||
|
BASE_CURRENT_C("baseCurrentC", "HI表:C相基波电流幅值,用于计算谐波电流含有率"),
|
||||||
|
STANDARD_RATE("standardRate", "HI表:标准源输出含有率"),
|
||||||
|
TEST_RATE_A("testRateA", "HI表:A相谐波电流含有率"),
|
||||||
|
TEST_RATE_B("testRateB", "HI表:B相谐波电流含有率"),
|
||||||
|
TEST_RATE_C("testRateC", "HI表:C相谐波电流含有率"),
|
||||||
|
ERROR_RATE_A("errorRateA", "HI表:A相谐波电流含有率误差"),
|
||||||
|
ERROR_RATE_B("errorRateB", "HI表:B相谐波电流含有率误差"),
|
||||||
|
ERROR_RATE_C("errorRateC", "HI表:C相谐波电流含有率误差"),
|
||||||
|
STANDARD_THD("standardThd", "HV/HI表:标准源总谐波畸变率"),
|
||||||
|
THD_A("thdA", "HV/HI表:A相总谐波畸变率"),
|
||||||
|
THD_B("thdB", "HV/HI表:B相总谐波畸变率"),
|
||||||
|
THD_C("thdC", "HV/HI表:C相总谐波畸变率");
|
||||||
|
|
||||||
private String key;
|
private String key;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public enum PowerIndexEnum {
|
|||||||
V("V", "电压"),
|
V("V", "电压"),
|
||||||
I("I", "电流"),
|
I("I", "电流"),
|
||||||
P("P", "功率"),
|
P("P", "功率"),
|
||||||
|
ANGLE("Angle", "基波相位"),
|
||||||
IMBV("IMBV", "负序电压不平衡度"),
|
IMBV("IMBV", "负序电压不平衡度"),
|
||||||
IMBA("IMBA", "负序电流不平衡度"),
|
IMBA("IMBA", "负序电流不平衡度"),
|
||||||
F("F", "短时电压闪变"),
|
F("F", "短时电压闪变"),
|
||||||
|
|||||||
@@ -865,6 +865,7 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
|||||||
Docx4jUtil.addWatermarkToDocument(baseModelDocument, "非正式");
|
Docx4jUtil.addWatermarkToDocument(baseModelDocument, "非正式");
|
||||||
}
|
}
|
||||||
Docx4jUtil.cleanBlankPagesAndRedundantPageBreaks(baseModelDocument);
|
Docx4jUtil.cleanBlankPagesAndRedundantPageBreaks(baseModelDocument);
|
||||||
|
Docx4jUtil.enableUpdateFieldsOnOpen(baseModelDocument);
|
||||||
baseModelDocument.save(new File(dirPath.concat(File.separator).concat(fileName)));
|
baseModelDocument.save(new File(dirPath.concat(File.separator).concat(fileName)));
|
||||||
this.updateDevAndPlanState(devId, devReportParam.getPlanId());
|
this.updateDevAndPlanState(devId, devReportParam.getPlanId());
|
||||||
} catch (NoSuchFileException e) {
|
} catch (NoSuchFileException e) {
|
||||||
@@ -945,6 +946,7 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
|||||||
String dirPath = pathConfig.getReportPath().concat(File.separator).concat(FilePathSanitizer.toSafeFileName(devType.getName()));
|
String dirPath = pathConfig.getReportPath().concat(File.separator).concat(FilePathSanitizer.toSafeFileName(devType.getName()));
|
||||||
// 确保目录存在
|
// 确保目录存在
|
||||||
ensureDirectoryExists(dirPath);
|
ensureDirectoryExists(dirPath);
|
||||||
|
Docx4jUtil.enableUpdateFieldsOnOpen(baseModelDocument);
|
||||||
baseModelDocument.save(new File(dirPath.concat(File.separator).concat(pqDevVO.getCreateId()).concat(ReportConstant.DOCX)));
|
baseModelDocument.save(new File(dirPath.concat(File.separator).concat(pqDevVO.getCreateId()).concat(ReportConstant.DOCX)));
|
||||||
this.updateDevAndPlanState(devId, devReportParam.getPlanId());
|
this.updateDevAndPlanState(devId, devReportParam.getPlanId());
|
||||||
} catch (NoSuchFileException e) {
|
} catch (NoSuchFileException e) {
|
||||||
@@ -1271,11 +1273,11 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
|||||||
// 检测脚本明细按 scriptCode 分组
|
// 检测脚本明细按 scriptCode 分组
|
||||||
List<PqScriptDtlDataVO> pqScriptDtlsList = pqScriptDtlsService.getScriptDtlsDataList(devReportParam.getScriptId());
|
List<PqScriptDtlDataVO> pqScriptDtlsList = pqScriptDtlsService.getScriptDtlsDataList(devReportParam.getScriptId());
|
||||||
Map<String, List<PqScriptDtlDataVO>> scriptMap = pqScriptDtlsList.stream()
|
Map<String, List<PqScriptDtlDataVO>> scriptMap = pqScriptDtlsList.stream()
|
||||||
.collect(Collectors.groupingBy(PqScriptDtlDataVO::getScriptCode, LinkedHashMap::new, Collectors.toList()));
|
.collect(Collectors.groupingBy(item -> itemAnchorGroupKey(item.getScriptCode()), LinkedHashMap::new, Collectors.toList()));
|
||||||
// detail 模板池按 H5 分组
|
// detail 模板池按 H5 分组
|
||||||
List<Docx4jUtil.HeadingContent> headingContents = Docx4jUtil.extractHeading5Contents(detailDocumentPart.getContent(), detailDocumentPart);
|
List<Docx4jUtil.HeadingContent> headingContents = Docx4jUtil.extractHeading5Contents(detailDocumentPart.getContent(), detailDocumentPart);
|
||||||
Map<String, List<Docx4jUtil.HeadingContent>> contentMap = headingContents.stream()
|
Map<String, List<Docx4jUtil.HeadingContent>> contentMap = headingContents.stream()
|
||||||
.collect(Collectors.groupingBy(Docx4jUtil.HeadingContent::getHeadingText, Collectors.toList()));
|
.collect(Collectors.groupingBy(item -> itemAnchorGroupKey(item.getHeadingText()), LinkedHashMap::new, Collectors.toList()));
|
||||||
int devChns = pqDevVO.getDevChns() == null ? 1 : pqDevVO.getDevChns();
|
int devChns = pqDevVO.getDevChns() == null ? 1 : pqDevVO.getDevChns();
|
||||||
Set<String> hiddenCodes = new LinkedHashSet<>();
|
Set<String> hiddenCodes = new LinkedHashSet<>();
|
||||||
for (BookmarkUtil.BookmarkInfo info : bookmarks) {
|
for (BookmarkUtil.BookmarkInfo info : bookmarks) {
|
||||||
@@ -1344,17 +1346,26 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
|||||||
ItemAnchorSectionUtil.stripItemAnchorBookmarks(baseDocumentPart);
|
ItemAnchorSectionUtil.stripItemAnchorBookmarks(baseDocumentPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String itemAnchorGroupKey(String value) {
|
||||||
|
return value == null ? "" : value.trim().toUpperCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新型模板的回路标题(仅多回路时被调用):"测量回路{n}",n 从 1 起。
|
* 新型模板的回路标题(仅多回路时被调用):"测量回路{n}",n 从 1 起。
|
||||||
* 与老路径 getLineTitle 的差异(有意):不带 "n." 前缀、单回路由调用方直接省略。
|
* 与老路径 getLineTitle 的差异(有意):不带 "n." 前缀、单回路由调用方直接省略。
|
||||||
*/
|
*/
|
||||||
private P buildItemLineTitle(Map<String, List<Docx4jUtil.HeadingContent>> contentMap, int lineIndex, ObjectFactory factory) {
|
private P buildItemLineTitle(Map<String, List<Docx4jUtil.HeadingContent>> contentMap, int lineIndex, ObjectFactory factory) {
|
||||||
int lineNo = lineIndex + 1;
|
int lineNo = lineIndex + 1;
|
||||||
if (CollUtil.isNotEmpty(contentMap.get(PowerIndexEnum.LINE_TITLE.getKey()))) {
|
|
||||||
return Docx4jUtil.createTitle(factory, 2, "测量回路" + lineNo, "SimSun", 30, true);
|
|
||||||
}
|
|
||||||
P titleParagraph = factory.createP();
|
P titleParagraph = factory.createP();
|
||||||
Docx4jUtil.createTitle(factory, titleParagraph, "测量回路" + lineNo, 28, true);
|
PPr pPr = factory.createPPr();
|
||||||
|
PPrBase.Spacing spacing = factory.createPPrBaseSpacing();
|
||||||
|
spacing.setBefore(BigInteger.valueOf(60));
|
||||||
|
spacing.setAfter(BigInteger.valueOf(20));
|
||||||
|
spacing.setLine(BigInteger.valueOf(240));
|
||||||
|
spacing.setLineRule(STLineSpacingRule.AUTO);
|
||||||
|
pPr.setSpacing(spacing);
|
||||||
|
titleParagraph.setPPr(pPr);
|
||||||
|
Docx4jUtil.createTitle(factory, titleParagraph, "测量回路" + lineNo, 24, true);
|
||||||
return titleParagraph;
|
return titleParagraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,9 @@ import com.njcn.common.pojo.exception.BusinessException;
|
|||||||
import com.njcn.gather.report.pojo.enums.ItemReportKeyEnum;
|
import com.njcn.gather.report.pojo.enums.ItemReportKeyEnum;
|
||||||
import com.njcn.gather.report.pojo.result.SingleTestResult;
|
import com.njcn.gather.report.pojo.result.SingleTestResult;
|
||||||
import com.njcn.gather.tools.report.util.Docx4jUtil;
|
import com.njcn.gather.tools.report.util.Docx4jUtil;
|
||||||
import org.docx4j.wml.BooleanDefaultTrue;
|
|
||||||
import org.docx4j.wml.HpsMeasure;
|
|
||||||
import org.docx4j.wml.Jc;
|
|
||||||
import org.docx4j.wml.JcEnumeration;
|
|
||||||
import org.docx4j.wml.ObjectFactory;
|
import org.docx4j.wml.ObjectFactory;
|
||||||
import org.docx4j.wml.P;
|
import org.docx4j.wml.P;
|
||||||
import org.docx4j.wml.PPr;
|
|
||||||
import org.docx4j.wml.R;
|
import org.docx4j.wml.R;
|
||||||
import org.docx4j.wml.RFonts;
|
|
||||||
import org.docx4j.wml.RPr;
|
import org.docx4j.wml.RPr;
|
||||||
import org.docx4j.wml.Tbl;
|
import org.docx4j.wml.Tbl;
|
||||||
import org.docx4j.wml.Tc;
|
import org.docx4j.wml.Tc;
|
||||||
@@ -25,11 +19,11 @@ import org.docx4j.wml.TrPr;
|
|||||||
|
|
||||||
import javax.xml.bind.JAXBElement;
|
import javax.xml.bind.JAXBElement;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,11 +57,15 @@ public final class ItemAnchorAssembler {
|
|||||||
if (templateTbl == null || CollUtil.isEmpty(results) || CollUtil.isEmpty(tableKeys)) {
|
if (templateTbl == null || CollUtil.isEmpty(results) || CollUtil.isEmpty(tableKeys)) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
int tableNo = 0;
|
||||||
for (SingleTestResult result : results) {
|
for (SingleTestResult result : results) {
|
||||||
List<Map<String, String>> rows = flattenRows(result, scriptCode);
|
List<Map<String, String>> rows = flattenRows(result, scriptCode);
|
||||||
if (rows.isEmpty()) {
|
if (rows.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
tableNo++;
|
||||||
|
applyHarmonicDerivedValues(rows, scriptCode);
|
||||||
|
Map<String, String> fixedPlaceholders = buildHarmonicFixedPlaceholders(rows, scriptCode);
|
||||||
JAXBElement<Tbl> copied;
|
JAXBElement<Tbl> copied;
|
||||||
try {
|
try {
|
||||||
copied = Docx4jUtil.deepCopyTbl(templateTbl);
|
copied = Docx4jUtil.deepCopyTbl(templateTbl);
|
||||||
@@ -75,6 +73,8 @@ public final class ItemAnchorAssembler {
|
|||||||
throw new RuntimeException("指标锚点模板表深拷贝失败: " + scriptCode, e);
|
throw new RuntimeException("指标锚点模板表深拷贝失败: " + scriptCode, e);
|
||||||
}
|
}
|
||||||
Tbl tbl = copied.getValue();
|
Tbl tbl = copied.getValue();
|
||||||
|
replaceFixedPlaceholders(tbl, fixedPlaceholders);
|
||||||
|
applyHarmonicPowerHeader(tbl, scriptCode, tableNo, rows);
|
||||||
List<Object> trs = tbl.getContent();
|
List<Object> trs = tbl.getContent();
|
||||||
// 模板规范约定:最后一行是 key 行(不走 judgeTableCross,新路径自带定位依据);
|
// 模板规范约定:最后一行是 key 行(不走 judgeTableCross,新路径自带定位依据);
|
||||||
// 从尾部找最后一个真正的 Tr——Word 重存可能把 bookmarkStart/End 漏成 w:tbl 直接子节点(本仓 _GoBack 先例)
|
// 从尾部找最后一个真正的 Tr——Word 重存可能把 bookmarkStart/End 漏成 w:tbl 直接子节点(本仓 _GoBack 先例)
|
||||||
@@ -121,14 +121,16 @@ public final class ItemAnchorAssembler {
|
|||||||
out.add(copied);
|
out.add(copied);
|
||||||
}
|
}
|
||||||
// 多表(谐波类多 scriptIndex)背靠背插入时,OOXML 相邻 w:tbl 会被 Word 合并渲染成一张大表;
|
// 多表(谐波类多 scriptIndex)背靠背插入时,OOXML 相邻 w:tbl 会被 Word 合并渲染成一张大表;
|
||||||
// 给每张表补"测点{n}"标题段分隔(客户源文档中多测点谐波表本就是带标题的独立表)。单表不加,输出不变
|
// 只插入空段落做结构分隔。测点号属于 HP 表内标题单元格,不能作为表外可见段落。
|
||||||
if (out.size() > 1) {
|
if (out.size() > 1) {
|
||||||
List<Object> withCaptions = new ArrayList<>(out.size() * 2);
|
List<Object> withSeparators = new ArrayList<>(out.size() * 2 - 1);
|
||||||
for (int i = 0; i < out.size(); i++) {
|
for (int i = 0; i < out.size(); i++) {
|
||||||
withCaptions.add(createPointCaption(factory, i + 1));
|
withSeparators.add(out.get(i));
|
||||||
withCaptions.add(out.get(i));
|
if (i < out.size() - 1) {
|
||||||
|
withSeparators.add(factory.createP());
|
||||||
}
|
}
|
||||||
return withCaptions;
|
}
|
||||||
|
return withSeparators;
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -182,37 +184,6 @@ public final class ItemAnchorAssembler {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** "测点{n}"表标题段:仿宋 9 号加粗居中,与客户源文档"谐波功率检验结果(测点n)"标题段同款式 */
|
|
||||||
private static P createPointCaption(ObjectFactory factory, int pointNo) {
|
|
||||||
P p = factory.createP();
|
|
||||||
PPr pPr = factory.createPPr();
|
|
||||||
Jc jc = factory.createJc();
|
|
||||||
jc.setVal(JcEnumeration.CENTER);
|
|
||||||
pPr.setJc(jc);
|
|
||||||
p.setPPr(pPr);
|
|
||||||
R run = factory.createR();
|
|
||||||
RPr rPr = factory.createRPr();
|
|
||||||
RFonts fonts = factory.createRFonts();
|
|
||||||
fonts.setAscii("仿宋");
|
|
||||||
fonts.setHAnsi("仿宋");
|
|
||||||
fonts.setEastAsia("仿宋");
|
|
||||||
fonts.setCs("仿宋");
|
|
||||||
rPr.setRFonts(fonts);
|
|
||||||
HpsMeasure sz = factory.createHpsMeasure();
|
|
||||||
sz.setVal(BigInteger.valueOf(18));
|
|
||||||
rPr.setSz(sz);
|
|
||||||
rPr.setSzCs(sz);
|
|
||||||
BooleanDefaultTrue bold = factory.createBooleanDefaultTrue();
|
|
||||||
rPr.setB(bold);
|
|
||||||
rPr.setBCs(bold);
|
|
||||||
run.setRPr(rPr);
|
|
||||||
Text text = factory.createText();
|
|
||||||
text.setValue("测点" + pointNo);
|
|
||||||
run.getContent().add(text);
|
|
||||||
p.getContent().add(run);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 把 SingleTestResult 的三层嵌套 detail 摊平成行列表,并做新模板专属的行级补充:
|
* 把 SingleTestResult 的三层嵌套 detail 摊平成行列表,并做新模板专属的行级补充:
|
||||||
* errorScope 注入(HV/HI/HP 的标准限值列)、time 缺失时"测点{n}"兜底(F/IMBV/IMBA)、P 有功特例。
|
* errorScope 注入(HV/HI/HP 的标准限值列)、time 缺失时"测点{n}"兜底(F/IMBV/IMBA)、P 有功特例。
|
||||||
@@ -239,7 +210,7 @@ public final class ItemAnchorAssembler {
|
|||||||
for (Map<String, String> raw : errorEntry.getValue()) {
|
for (Map<String, String> raw : errorEntry.getValue()) {
|
||||||
seq++;
|
seq++;
|
||||||
Map<String, String> row = new LinkedHashMap<>(raw);
|
Map<String, String> row = new LinkedHashMap<>(raw);
|
||||||
row.putIfAbsent(ItemReportKeyEnum.ERROR_SCOPE.getKey(), errorScope);
|
applyErrorScope(row, scriptCode, errorScope);
|
||||||
if (StrUtil.isBlank(row.get(ItemReportKeyEnum.TIME.getKey()))) {
|
if (StrUtil.isBlank(row.get(ItemReportKeyEnum.TIME.getKey()))) {
|
||||||
row.put(ItemReportKeyEnum.TIME.getKey(), "测点" + seq);
|
row.put(ItemReportKeyEnum.TIME.getKey(), "测点" + seq);
|
||||||
}
|
}
|
||||||
@@ -254,6 +225,23 @@ public final class ItemAnchorAssembler {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void applyErrorScope(Map<String, String> row, String scriptCode, String errorScope) {
|
||||||
|
String key = ItemReportKeyEnum.ERROR_SCOPE.getKey();
|
||||||
|
if ("HV".equalsIgnoreCase(scriptCode) || "HP".equalsIgnoreCase(scriptCode)) {
|
||||||
|
String value = StrUtil.isBlank(row.get(key)) ? errorScope : row.get(key);
|
||||||
|
row.put(key, stripTrailingUnit(value));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
row.putIfAbsent(key, errorScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String stripTrailingUnit(String value) {
|
||||||
|
if (StrUtil.isBlank(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return value.trim().replaceFirst("\\s*(?:(?:[%%‰°℃]|[a-zA-Z]+|[\\u4e00-\\u9fa5]+)\\s*)+$", "");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* P(有功功率)特例:activePower = 三相实测之和;设置类与无功/视在/功率因数数据侧暂无,留空。
|
* P(有功功率)特例:activePower = 三相实测之和;设置类与无功/视在/功率因数数据侧暂无,留空。
|
||||||
* spec §6 #2 处置。
|
* spec §6 #2 处置。
|
||||||
@@ -279,6 +267,256 @@ public final class ItemAnchorAssembler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void applyHarmonicDerivedValues(List<Map<String, String>> rows, String scriptCode) {
|
||||||
|
if (!"HI".equalsIgnoreCase(scriptCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Map<String, String> row : rows) {
|
||||||
|
String scriptStandardRate = row.get(ItemReportKeyEnum.STANDARD_RATE.getKey());
|
||||||
|
String standardRate = parseNumber(scriptStandardRate) == null ? "" : scriptStandardRate;
|
||||||
|
String testRateA = rate(row.get(ItemReportKeyEnum.TEST_A.getKey()), row.get(ItemReportKeyEnum.BASE_CURRENT_A.getKey()));
|
||||||
|
String testRateB = rate(row.get(ItemReportKeyEnum.TEST_B.getKey()), row.get(ItemReportKeyEnum.BASE_CURRENT_B.getKey()));
|
||||||
|
String testRateC = rate(row.get(ItemReportKeyEnum.TEST_C.getKey()), row.get(ItemReportKeyEnum.BASE_CURRENT_C.getKey()));
|
||||||
|
String errorRateA = difference(testRateA, standardRate);
|
||||||
|
String errorRateB = difference(testRateB, standardRate);
|
||||||
|
String errorRateC = difference(testRateC, standardRate);
|
||||||
|
row.put(ItemReportKeyEnum.STANDARD_RATE.getKey(), standardRate);
|
||||||
|
row.put(ItemReportKeyEnum.TEST_RATE_A.getKey(), testRateA);
|
||||||
|
row.put(ItemReportKeyEnum.TEST_RATE_B.getKey(), testRateB);
|
||||||
|
row.put(ItemReportKeyEnum.TEST_RATE_C.getKey(), testRateC);
|
||||||
|
row.put(ItemReportKeyEnum.ERROR_RATE_A.getKey(), errorRateA);
|
||||||
|
row.put(ItemReportKeyEnum.ERROR_RATE_B.getKey(), errorRateB);
|
||||||
|
row.put(ItemReportKeyEnum.ERROR_RATE_C.getKey(), errorRateC);
|
||||||
|
row.put(ItemReportKeyEnum.ERROR_SCOPE.getKey(), harmonicCurrentErrorScope(standardRate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String harmonicCurrentErrorScope(String standardRateText) {
|
||||||
|
Double standardRate = parseNumber(standardRateText);
|
||||||
|
if (standardRate == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
double limit = standardRate >= 3D ? standardRate * 0.05D : 0.15D;
|
||||||
|
return "±" + format4(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> buildHarmonicFixedPlaceholders(List<Map<String, String>> rows, String scriptCode) {
|
||||||
|
Map<String, String> placeholders = new LinkedHashMap<>();
|
||||||
|
if ("HV".equalsIgnoreCase(scriptCode)) {
|
||||||
|
placeholders.put(ItemReportKeyEnum.STANDARD_THD.getKey(), thd(rows, ItemReportKeyEnum.STANDARD.getKey()));
|
||||||
|
placeholders.put(ItemReportKeyEnum.THD_A.getKey(), thd(rows, ItemReportKeyEnum.TEST_A.getKey()));
|
||||||
|
placeholders.put(ItemReportKeyEnum.THD_B.getKey(), thd(rows, ItemReportKeyEnum.TEST_B.getKey()));
|
||||||
|
placeholders.put(ItemReportKeyEnum.THD_C.getKey(), thd(rows, ItemReportKeyEnum.TEST_C.getKey()));
|
||||||
|
} else if ("HI".equalsIgnoreCase(scriptCode)) {
|
||||||
|
placeholders.put(ItemReportKeyEnum.STANDARD_THD.getKey(), thd(rows, ItemReportKeyEnum.STANDARD_RATE.getKey()));
|
||||||
|
placeholders.put(ItemReportKeyEnum.THD_A.getKey(), thd(rows, ItemReportKeyEnum.TEST_RATE_A.getKey()));
|
||||||
|
placeholders.put(ItemReportKeyEnum.THD_B.getKey(), thd(rows, ItemReportKeyEnum.TEST_RATE_B.getKey()));
|
||||||
|
placeholders.put(ItemReportKeyEnum.THD_C.getKey(), thd(rows, ItemReportKeyEnum.TEST_RATE_C.getKey()));
|
||||||
|
}
|
||||||
|
return placeholders;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyHarmonicPowerHeader(Tbl tbl, String scriptCode, int pointNo, List<Map<String, String>> rows) {
|
||||||
|
if (!"HP".equalsIgnoreCase(scriptCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Tr firstRow = findFirstRow(tbl.getContent());
|
||||||
|
if (firstRow == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Tc titleCell = cellAt(firstRow, 0);
|
||||||
|
if (titleCell == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String title = Docx4jUtil.getTextFromCell(titleCell);
|
||||||
|
String baseTitle = StrUtil.blankToDefault(title, StrUtil.EMPTY)
|
||||||
|
.replaceFirst("(测点\\s*\\d+)$", "");
|
||||||
|
setCellText(titleCell, baseTitle + "(测点" + pointNo + ")");
|
||||||
|
fillNextCellByLabel(tbl, "基波电压", firstDataValue(rows, ItemReportKeyEnum.STANDARD_V.getKey()));
|
||||||
|
fillNextCellByLabel(tbl, "基波电流", firstDataValue(rows, ItemReportKeyEnum.STANDARD_I.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String firstDataValue(List<Map<String, String>> rows, String key) {
|
||||||
|
if (CollUtil.isEmpty(rows)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
for (Map<String, String> row : rows) {
|
||||||
|
String value = row.get(key);
|
||||||
|
if (StrUtil.isNotBlank(value) && !"/".equals(value.trim())) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fillNextCellByLabel(Tbl tbl, String label, String value) {
|
||||||
|
if (StrUtil.isBlank(value) || tbl == null || tbl.getContent() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Object o : tbl.getContent()) {
|
||||||
|
Object v = o instanceof JAXBElement ? ((JAXBElement<?>) o).getValue() : o;
|
||||||
|
if (v instanceof Tr && fillNextCellByLabel((Tr) v, label, value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean fillNextCellByLabel(Tr row, String label, String value) {
|
||||||
|
if (StrUtil.isBlank(value) || row == null || row.getContent() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<Object> cells = row.getContent();
|
||||||
|
for (int i = 0; i < cells.size() - 1; i++) {
|
||||||
|
Tc labelCell = cellAt(row, i);
|
||||||
|
if (labelCell != null && Docx4jUtil.getTextFromCell(labelCell).contains(label)) {
|
||||||
|
Tc valueCell = cellAt(row, i + 1);
|
||||||
|
if (valueCell != null) {
|
||||||
|
setCellText(valueCell, value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Tr findFirstRow(List<Object> content) {
|
||||||
|
for (Object o : content) {
|
||||||
|
Object v = o instanceof JAXBElement ? ((JAXBElement<?>) o).getValue() : o;
|
||||||
|
if (v instanceof Tr) {
|
||||||
|
return (Tr) v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Tc cellAt(Tr row, int index) {
|
||||||
|
if (row == null || row.getContent() == null || row.getContent().size() <= index) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Object cellObj = row.getContent().get(index);
|
||||||
|
Object value = cellObj instanceof JAXBElement ? ((JAXBElement<?>) cellObj).getValue() : cellObj;
|
||||||
|
return value instanceof Tc ? (Tc) value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String rate(String numeratorText, String denominatorText) {
|
||||||
|
Double numerator = parseNumber(numeratorText);
|
||||||
|
Double denominator = parseNumber(denominatorText);
|
||||||
|
if (numerator == null || denominator == null || denominator == 0D) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return format4(numerator / denominator * 100D);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String difference(String minuendText, String subtrahendText) {
|
||||||
|
Double minuend = parseNumber(minuendText);
|
||||||
|
Double subtrahend = parseNumber(subtrahendText);
|
||||||
|
if (minuend == null || subtrahend == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return format4(minuend - subtrahend);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String thd(List<Map<String, String>> rows, String key) {
|
||||||
|
double sum = 0D;
|
||||||
|
boolean hasValue = false;
|
||||||
|
for (Map<String, String> row : rows) {
|
||||||
|
Double value = parseNumber(row.get(key));
|
||||||
|
if (value != null) {
|
||||||
|
sum += value * value;
|
||||||
|
hasValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasValue ? format4(Math.sqrt(sum)) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Double parseNumber(String text) {
|
||||||
|
if (StrUtil.isBlank(text) || "/".equals(text.trim()) || "--".equals(text.trim())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Double.parseDouble(text.trim());
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format4(double value) {
|
||||||
|
return String.format(Locale.ROOT, "%.4f", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void replaceFixedPlaceholders(Tbl tbl, Map<String, String> values) {
|
||||||
|
if (values.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Object rowObj : tbl.getContent()) {
|
||||||
|
Object rowValue = rowObj instanceof JAXBElement ? ((JAXBElement<?>) rowObj).getValue() : rowObj;
|
||||||
|
if (!(rowValue instanceof Tr)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (Object cellObj : ((Tr) rowValue).getContent()) {
|
||||||
|
Object cellValue = cellObj instanceof JAXBElement ? ((JAXBElement<?>) cellObj).getValue() : cellObj;
|
||||||
|
if (cellValue instanceof Tc) {
|
||||||
|
Tc cell = (Tc) cellValue;
|
||||||
|
String replacement = values.get(Docx4jUtil.getTextFromCell(cell));
|
||||||
|
if (replacement != null) {
|
||||||
|
setCellText(cell, replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setCellText(Tc cell, String value) {
|
||||||
|
P firstParagraph = null;
|
||||||
|
for (Object content : cell.getContent()) {
|
||||||
|
if (content instanceof P) {
|
||||||
|
firstParagraph = (P) content;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (firstParagraph == null) {
|
||||||
|
firstParagraph = new P();
|
||||||
|
cell.getContent().add(firstParagraph);
|
||||||
|
}
|
||||||
|
boolean written = false;
|
||||||
|
for (Object paragraphObj : cell.getContent()) {
|
||||||
|
if (!(paragraphObj instanceof P)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
P paragraph = (P) paragraphObj;
|
||||||
|
for (Object runObj : paragraph.getContent()) {
|
||||||
|
if (!(runObj instanceof R)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
R run = (R) runObj;
|
||||||
|
for (Object textObj : run.getContent()) {
|
||||||
|
Text text = unwrapText(textObj);
|
||||||
|
if (text != null) {
|
||||||
|
text.setValue(written ? "" : value);
|
||||||
|
written = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!written) {
|
||||||
|
R run = new R();
|
||||||
|
Text text = new Text();
|
||||||
|
text.setValue(value);
|
||||||
|
run.getContent().add(text);
|
||||||
|
firstParagraph.getContent().add(run);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Text unwrapText(Object textObj) {
|
||||||
|
if (textObj instanceof Text) {
|
||||||
|
return (Text) textObj;
|
||||||
|
}
|
||||||
|
if (textObj instanceof JAXBElement && ((JAXBElement<?>) textObj).getValue() instanceof Text) {
|
||||||
|
return (Text) ((JAXBElement<?>) textObj).getValue();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static JAXBElement<Tbl> findFirstTable(Docx4jUtil.HeadingContent template) {
|
private static JAXBElement<Tbl> findFirstTable(Docx4jUtil.HeadingContent template) {
|
||||||
if (template == null || CollUtil.isEmpty(template.getSubContent())) {
|
if (template == null || CollUtil.isEmpty(template.getSubContent())) {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ import com.njcn.gather.result.pojo.param.ResultParam;
|
|||||||
import com.njcn.gather.result.pojo.vo.*;
|
import com.njcn.gather.result.pojo.vo.*;
|
||||||
import com.njcn.gather.result.service.DataCheckAsyncNotifier;
|
import com.njcn.gather.result.service.DataCheckAsyncNotifier;
|
||||||
import com.njcn.gather.result.service.IResultService;
|
import com.njcn.gather.result.service.IResultService;
|
||||||
|
import com.njcn.gather.result.util.AngleResultAssembler;
|
||||||
import com.njcn.gather.result.util.PowerResultAssembler;
|
import com.njcn.gather.result.util.PowerResultAssembler;
|
||||||
import com.njcn.gather.result.util.UnbalanceResultAssembler;
|
import com.njcn.gather.result.util.UnbalanceResultAssembler;
|
||||||
import com.njcn.gather.script.mapper.PqScriptMapper;
|
import com.njcn.gather.script.mapper.PqScriptMapper;
|
||||||
@@ -1469,6 +1470,22 @@ public class ResultServiceImpl implements IResultService {
|
|||||||
finalContent.put(affectName, errorList);
|
finalContent.put(affectName, errorList);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (PowerIndexEnum.ANGLE.getKey().equalsIgnoreCase(scriptCode)) {
|
||||||
|
SingleNonHarmParam param = new SingleNonHarmParam(planCode, devId, lineNo, valueTypeList, indexList);
|
||||||
|
List<SimAndDigNonHarmonicResult> angleResults = simAndDigNonHarmonicService.queryBySortListWithAdTypeCode(param);
|
||||||
|
List<Map<String, String>> keyFillMapList = AngleResultAssembler.assembleRows(angleResults);
|
||||||
|
if (CollUtil.isEmpty(keyFillMapList)) {
|
||||||
|
log.warn("生成基波相位类表格数据跳过,结果表数据为空,planCode={}, devId={}, lineNo={}, scriptCode={}, scriptIndexList={}",
|
||||||
|
planCode, devId, lineNo, scriptCode, indexList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<String, List<Map<String, String>>> errorScoperMap = keyFillMapList.stream()
|
||||||
|
.collect(Collectors.groupingBy(map -> map.get(ItemReportKeyEnum.ERROR_SCOPE.getKey()), LinkedHashMap::new, Collectors.toList()));
|
||||||
|
List<Map<String, List<Map<String, String>>>> errorList = new ArrayList<>();
|
||||||
|
errorList.add(errorScoperMap);
|
||||||
|
finalContent.put(affectName, errorList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (PowerIndexEnum.IMBV.getKey().equalsIgnoreCase(scriptCode)
|
if (PowerIndexEnum.IMBV.getKey().equalsIgnoreCase(scriptCode)
|
||||||
|| PowerIndexEnum.IMBA.getKey().equalsIgnoreCase(scriptCode)) {
|
|| PowerIndexEnum.IMBA.getKey().equalsIgnoreCase(scriptCode)) {
|
||||||
SingleNonHarmParam param = new SingleNonHarmParam(planCode, devId, lineNo, valueTypeList, indexList);
|
SingleNonHarmParam param = new SingleNonHarmParam(planCode, devId, lineNo, valueTypeList, indexList);
|
||||||
@@ -1552,6 +1569,8 @@ public class ResultServiceImpl implements IResultService {
|
|||||||
int timeInt = (int) timeDouble;
|
int timeInt = (int) timeDouble;
|
||||||
// 填充结果数据
|
// 填充结果数据
|
||||||
if (fillThreePhaseData(singleResult, timeInt, keyFillMap, scriptCode)) {
|
if (fillThreePhaseData(singleResult, timeInt, keyFillMap, scriptCode)) {
|
||||||
|
fillHarmonicScriptStandard(keyFillMap, scriptCode, dtlsList);
|
||||||
|
fillHarmonicPowerSourceSettings(keyFillMap, scriptCode, scriptDtlDataVOList);
|
||||||
keyFillMapList.add(keyFillMap);
|
keyFillMapList.add(keyFillMap);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1754,9 +1773,100 @@ public class ResultServiceImpl implements IResultService {
|
|||||||
keyFillMap.put(ItemReportKeyEnum.RESULT.getKey(), result);
|
keyFillMap.put(ItemReportKeyEnum.RESULT.getKey(), result);
|
||||||
errorScope = dealErrorScope(errorScope).concat(unit);
|
errorScope = dealErrorScope(errorScope).concat(unit);
|
||||||
keyFillMap.put(ItemReportKeyEnum.ERROR_SCOPE.getKey(), errorScope);
|
keyFillMap.put(ItemReportKeyEnum.ERROR_SCOPE.getKey(), errorScope);
|
||||||
|
if (PowerIndexEnum.HI.getKey().equalsIgnoreCase(scriptCode)) {
|
||||||
|
fillBaseCurrentData(singleResult, keyFillMap);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HI 新模板明细显示谐波电流含有率,装配期需要同一条谐波结果中的三相基波电流作为分母。
|
||||||
|
*/
|
||||||
|
private void fillBaseCurrentData(Object singleResult, Map<String, String> keyFillMap) {
|
||||||
|
putBaseCurrent(singleResult, PowerConstant.PHASE_A, ItemReportKeyEnum.BASE_CURRENT_A.getKey(), keyFillMap);
|
||||||
|
putBaseCurrent(singleResult, PowerConstant.PHASE_B, ItemReportKeyEnum.BASE_CURRENT_B.getKey(), keyFillMap);
|
||||||
|
putBaseCurrent(singleResult, PowerConstant.PHASE_C, ItemReportKeyEnum.BASE_CURRENT_C.getKey(), keyFillMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putBaseCurrent(Object singleResult, String phase, String testKey, Map<String, String> keyFillMap) {
|
||||||
|
DetectionData base = getResultData(singleResult, 1, phase);
|
||||||
|
keyFillMap.put(testKey, formatBaseCurrent(base == null ? null : base.getData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatBaseCurrent(Double value) {
|
||||||
|
String formatted = value == null ? "" : PubUtils.doubleRoundStr(4, value);
|
||||||
|
return formatted == null ? "" : formatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HI 的标准源输出值在云南模板中展示含有率,优先沿用脚本下发的百分比值。
|
||||||
|
*/
|
||||||
|
private void fillHarmonicScriptStandard(Map<String, String> keyFillMap, String scriptCode, List<PqScriptCheckData> checkDataList) {
|
||||||
|
if (!PowerIndexEnum.HI.getKey().equalsIgnoreCase(scriptCode) || CollUtil.isEmpty(checkDataList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(keyFillMap.get(ItemReportKeyEnum.STANDARD_RATE.getKey()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PqScriptCheckData standard = firstCheckValue(checkDataList);
|
||||||
|
if (standard == null || standard.getValue() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String value = PubUtils.doubleRoundStr(4, standard.getValue());
|
||||||
|
if (value != null) {
|
||||||
|
keyFillMap.put(ItemReportKeyEnum.STANDARD_RATE.getKey(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PqScriptCheckData firstCheckValue(List<PqScriptCheckData> checkDataList) {
|
||||||
|
for (String phase : Arrays.asList(PowerConstant.PHASE_A, PowerConstant.PHASE_B, PowerConstant.PHASE_C)) {
|
||||||
|
for (PqScriptCheckData data : checkDataList) {
|
||||||
|
if (phase.equalsIgnoreCase(data.getPhase()) && data.getValue() != null) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkDataList.stream().filter(data -> data.getValue() != null).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HP 新模板表头需要基波电压/基波电流设置值,来源是同一 scriptIndex 的 VOL/CUR 脚本明细。
|
||||||
|
*/
|
||||||
|
private void fillHarmonicPowerSourceSettings(Map<String, String> keyFillMap, String scriptCode,
|
||||||
|
List<? extends PqScriptDtls> scriptDetails) {
|
||||||
|
if (!PowerIndexEnum.HP.getKey().equalsIgnoreCase(scriptCode) || CollUtil.isEmpty(scriptDetails)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
putScriptSetting(keyFillMap, ItemReportKeyEnum.STANDARD_V.getKey(), firstScriptSetting(scriptDetails, "VOL"));
|
||||||
|
putScriptSetting(keyFillMap, ItemReportKeyEnum.STANDARD_I.getKey(), firstScriptSetting(scriptDetails, "CUR"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putScriptSetting(Map<String, String> keyFillMap, String key, PqScriptDtls detail) {
|
||||||
|
if (detail == null || detail.getValue() == null || StrUtil.isNotBlank(keyFillMap.get(key))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String value = PubUtils.doubleRoundStr(4, detail.getValue());
|
||||||
|
if (StrUtil.isNotBlank(value)) {
|
||||||
|
keyFillMap.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PqScriptDtls firstScriptSetting(List<? extends PqScriptDtls> scriptDetails, String valueType) {
|
||||||
|
for (String phase : Arrays.asList(PowerConstant.PHASE_A, PowerConstant.PHASE_B, PowerConstant.PHASE_C)) {
|
||||||
|
for (PqScriptDtls detail : scriptDetails) {
|
||||||
|
if (valueType.equalsIgnoreCase(detail.getValueType())
|
||||||
|
&& phase.equalsIgnoreCase(detail.getPhase())
|
||||||
|
&& detail.getValue() != null) {
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scriptDetails.stream()
|
||||||
|
.filter(detail -> valueType.equalsIgnoreCase(detail.getValueType()) && detail.getValue() != null)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 针对浙江杭州处理脚本输出细节,todo... 需要更改更合理的方式,现在算是写死的了部分
|
* 针对浙江杭州处理脚本输出细节,todo... 需要更改更合理的方式,现在算是写死的了部分
|
||||||
* 目前浙江这边就是(间)谐波电压/电流
|
* 目前浙江这边就是(间)谐波电压/电流
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
package com.njcn.gather.result.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.njcn.gather.detection.pojo.enums.DetectionCodeEnum;
|
||||||
|
import com.njcn.gather.detection.pojo.vo.DetectionData;
|
||||||
|
import com.njcn.gather.report.pojo.enums.ItemReportKeyEnum;
|
||||||
|
import com.njcn.gather.storage.pojo.po.SimAndDigNonHarmonicResult;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基波相位报告结果行装配:云南模板以电压相角为 0 度基准,取 I1A 的 A/B/C 三相电流相角填表。
|
||||||
|
*/
|
||||||
|
public final class AngleResultAssembler {
|
||||||
|
|
||||||
|
private AngleResultAssembler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Map<String, String>> assembleRows(List<SimAndDigNonHarmonicResult> results) {
|
||||||
|
List<Map<String, String>> rows = new ArrayList<>();
|
||||||
|
if (CollUtil.isEmpty(results)) {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
Map<Integer, List<SimAndDigNonHarmonicResult>> bySort = results.stream()
|
||||||
|
.filter(result -> result.getSort() != null)
|
||||||
|
.collect(Collectors.groupingBy(SimAndDigNonHarmonicResult::getSort, TreeMap::new, Collectors.toList()));
|
||||||
|
for (List<SimAndDigNonHarmonicResult> sameSortResults : bySort.values()) {
|
||||||
|
SimAndDigNonHarmonicResult currentAngle = firstCurrentAngle(sameSortResults);
|
||||||
|
if (currentAngle == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DetectionData[] phases = phases(currentAngle);
|
||||||
|
DetectionData primary = firstPresent(phases);
|
||||||
|
if (primary == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Map<String, String> row = defaultAngleRow();
|
||||||
|
row.put(ItemReportKeyEnum.STANDARD.getKey(), valueOrSlash(primary.getResultData()));
|
||||||
|
putPhaseDetails(row, phases);
|
||||||
|
row.put(ItemReportKeyEnum.ERROR_SCOPE.getKey(), errorScope(primary));
|
||||||
|
row.put(ItemReportKeyEnum.RESULT.getKey(), overallResult(phases));
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SimAndDigNonHarmonicResult firstCurrentAngle(List<SimAndDigNonHarmonicResult> results) {
|
||||||
|
if (CollUtil.isEmpty(results)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (SimAndDigNonHarmonicResult result : results) {
|
||||||
|
if (DetectionCodeEnum.I1A.getCode().equalsIgnoreCase(result.getAdType())) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> defaultAngleRow() {
|
||||||
|
Map<String, String> row = new LinkedHashMap<>();
|
||||||
|
for (ItemReportKeyEnum key : Arrays.asList(
|
||||||
|
ItemReportKeyEnum.STANDARD,
|
||||||
|
ItemReportKeyEnum.TEST_A,
|
||||||
|
ItemReportKeyEnum.TEST_B,
|
||||||
|
ItemReportKeyEnum.TEST_C,
|
||||||
|
ItemReportKeyEnum.ERROR_A,
|
||||||
|
ItemReportKeyEnum.ERROR_B,
|
||||||
|
ItemReportKeyEnum.ERROR_C,
|
||||||
|
ItemReportKeyEnum.ERROR_SCOPE,
|
||||||
|
ItemReportKeyEnum.RESULT,
|
||||||
|
ItemReportKeyEnum.RESULT_A,
|
||||||
|
ItemReportKeyEnum.RESULT_B,
|
||||||
|
ItemReportKeyEnum.RESULT_C)) {
|
||||||
|
row.put(key.getKey(), "/");
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DetectionData[] phases(SimAndDigNonHarmonicResult result) {
|
||||||
|
return new DetectionData[]{
|
||||||
|
parse(result.getAValue()),
|
||||||
|
parse(result.getBValue()),
|
||||||
|
parse(result.getCValue())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void putPhaseDetails(Map<String, String> row, DetectionData[] phases) {
|
||||||
|
List<String> tests = Arrays.asList(ItemReportKeyEnum.TEST_A.getKey(),
|
||||||
|
ItemReportKeyEnum.TEST_B.getKey(), ItemReportKeyEnum.TEST_C.getKey());
|
||||||
|
List<String> errors = Arrays.asList(ItemReportKeyEnum.ERROR_A.getKey(),
|
||||||
|
ItemReportKeyEnum.ERROR_B.getKey(), ItemReportKeyEnum.ERROR_C.getKey());
|
||||||
|
List<String> results = Arrays.asList(ItemReportKeyEnum.RESULT_A.getKey(),
|
||||||
|
ItemReportKeyEnum.RESULT_B.getKey(), ItemReportKeyEnum.RESULT_C.getKey());
|
||||||
|
for (int i = 0; i < phases.length; i++) {
|
||||||
|
DetectionData phase = phases[i];
|
||||||
|
row.put(tests.get(i), phase == null ? "/" : valueOrSlash(phase.getData()));
|
||||||
|
row.put(errors.get(i), phase == null || phase.getErrorData() == null ? "/" : format(phase.getErrorData()));
|
||||||
|
row.put(results.get(i), phase == null ? "/" : resultText(phase.getIsData()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DetectionData firstPresent(DetectionData[] values) {
|
||||||
|
for (DetectionData value : values) {
|
||||||
|
if (value != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DetectionData parse(String value) {
|
||||||
|
if (StrUtil.isBlank(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return JSONUtil.toBean(value, DetectionData.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String overallResult(DetectionData[] phases) {
|
||||||
|
boolean hasResult = false;
|
||||||
|
for (DetectionData phase : phases) {
|
||||||
|
if (phase == null || phase.getIsData() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hasResult = true;
|
||||||
|
if (phase.getIsData() == 2) {
|
||||||
|
return "不合格";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasResult ? "合格" : "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resultText(Integer isData) {
|
||||||
|
if (isData == null) {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
if (isData == 1) {
|
||||||
|
return "合格";
|
||||||
|
}
|
||||||
|
if (isData == 2) {
|
||||||
|
return "不合格";
|
||||||
|
}
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String errorScope(DetectionData primary) {
|
||||||
|
String scope = dealErrorScope(primary.getRadius());
|
||||||
|
if (StrUtil.isNotBlank(primary.getUnit()) && !"/".equals(scope)) {
|
||||||
|
return scope + primary.getUnit();
|
||||||
|
}
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String dealErrorScope(String errorScope) {
|
||||||
|
if (StrUtil.isBlank(errorScope)) {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
if (errorScope.contains("~")) {
|
||||||
|
String[] split = errorScope.split("~");
|
||||||
|
String begin = split[0];
|
||||||
|
if (begin.startsWith("-")) {
|
||||||
|
begin = begin.substring(1);
|
||||||
|
}
|
||||||
|
if (split.length > 1 && split[1].equalsIgnoreCase(begin)) {
|
||||||
|
return "±" + begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errorScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String valueOrSlash(Double value) {
|
||||||
|
return value == null ? "/" : format(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(Double value) {
|
||||||
|
return value == null ? "/" : BigDecimal.valueOf(value).setScale(4, RoundingMode.HALF_UP).toPlainString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(BigDecimal value) {
|
||||||
|
return value == null ? "/" : value.setScale(4, RoundingMode.HALF_UP).toPlainString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,7 +58,6 @@ public final class UnbalanceResultAssembler {
|
|||||||
private static Map<String, String> defaultRow() {
|
private static Map<String, String> defaultRow() {
|
||||||
Map<String, String> row = new LinkedHashMap<>();
|
Map<String, String> row = new LinkedHashMap<>();
|
||||||
for (ItemReportKeyEnum key : Arrays.asList(
|
for (ItemReportKeyEnum key : Arrays.asList(
|
||||||
ItemReportKeyEnum.TIME,
|
|
||||||
ItemReportKeyEnum.STANDARD,
|
ItemReportKeyEnum.STANDARD,
|
||||||
ItemReportKeyEnum.TEST,
|
ItemReportKeyEnum.TEST,
|
||||||
ItemReportKeyEnum.ERROR,
|
ItemReportKeyEnum.ERROR,
|
||||||
|
|||||||
@@ -1,10 +1,24 @@
|
|||||||
package com.njcn.gather.report.service.impl;
|
package com.njcn.gather.report.service.impl;
|
||||||
|
|
||||||
|
import com.njcn.gather.report.pojo.enums.PowerIndexEnum;
|
||||||
|
import com.njcn.gather.tools.report.util.Docx4jUtil;
|
||||||
|
import org.docx4j.wml.ObjectFactory;
|
||||||
|
import org.docx4j.wml.P;
|
||||||
|
import org.docx4j.wml.R;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
public class PqReportServiceImplTest {
|
public class PqReportServiceImplTest {
|
||||||
|
|
||||||
@@ -22,4 +36,39 @@ public class PqReportServiceImplTest {
|
|||||||
public void formatNumberChineseDateUsesChineseDateSeparators() {
|
public void formatNumberChineseDateUsesChineseDateSeparators() {
|
||||||
assertEquals("2021年1月1日", PqReportServiceImpl.formatNumberChineseDate(LocalDate.of(2021, 1, 1)));
|
assertEquals("2021年1月1日", PqReportServiceImpl.formatNumberChineseDate(LocalDate.of(2021, 1, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildItemLineTitleCreatesNormalSmallParagraph() throws Exception {
|
||||||
|
PqReportServiceImpl service = newReportService();
|
||||||
|
ObjectFactory factory = new ObjectFactory();
|
||||||
|
Docx4jUtil.HeadingContent lineTitleTemplate = new Docx4jUtil.HeadingContent();
|
||||||
|
lineTitleTemplate.setHeadingText(PowerIndexEnum.LINE_TITLE.getKey());
|
||||||
|
Map<String, List<Docx4jUtil.HeadingContent>> contentMap = new HashMap<>();
|
||||||
|
contentMap.put(PowerIndexEnum.LINE_TITLE.getKey(), Collections.singletonList(lineTitleTemplate));
|
||||||
|
|
||||||
|
Method method = PqReportServiceImpl.class.getDeclaredMethod("buildItemLineTitle", Map.class, int.class, ObjectFactory.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
P paragraph = (P) method.invoke(service, contentMap, 0, factory);
|
||||||
|
|
||||||
|
assertEquals("测量回路1", Docx4jUtil.getTextFromP(paragraph));
|
||||||
|
assertNull(paragraph.getPPr().getOutlineLvl());
|
||||||
|
R run = (R) paragraph.getContent().get(0);
|
||||||
|
assertNotNull(run.getRPr());
|
||||||
|
assertEquals(BigInteger.valueOf(24), run.getRPr().getSz().getVal());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void itemAnchorGroupKeyNormalizesAngleScriptCode() throws Exception {
|
||||||
|
Method method = PqReportServiceImpl.class.getDeclaredMethod("itemAnchorGroupKey", String.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
|
||||||
|
assertEquals("ANGLE", method.invoke(null, "Angle"));
|
||||||
|
assertEquals("ANGLE", method.invoke(null, "ANGLE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PqReportServiceImpl newReportService() throws Exception {
|
||||||
|
Constructor<?> constructor = PqReportServiceImpl.class.getDeclaredConstructors()[0];
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
return (PqReportServiceImpl) constructor.newInstance(new Object[constructor.getParameterCount()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.njcn.gather.report.util;
|
||||||
|
|
||||||
|
import com.njcn.gather.tools.report.util.Docx4jUtil;
|
||||||
|
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||||
|
import org.docx4j.openpackaging.parts.WordprocessingML.DocumentSettingsPart;
|
||||||
|
import org.docx4j.wml.CTSettings;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class Docx4jUtilUpdateFieldsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableUpdateFieldsOnOpenSetsWordSettingsFlag() throws Exception {
|
||||||
|
WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage();
|
||||||
|
|
||||||
|
Docx4jUtil.enableUpdateFieldsOnOpen(wordPackage);
|
||||||
|
|
||||||
|
DocumentSettingsPart settingsPart = wordPackage.getMainDocumentPart().getDocumentSettingsPart();
|
||||||
|
assertNotNull(settingsPart);
|
||||||
|
CTSettings settings = settingsPart.getContents();
|
||||||
|
assertNotNull(settings.getUpdateFields());
|
||||||
|
assertTrue(settings.getUpdateFields().isVal());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,10 +75,10 @@ public class ItemAnchorAssemblerTest {
|
|||||||
private Docx4jUtil.HeadingContent hvTemplate(String... keys) {
|
private Docx4jUtil.HeadingContent hvTemplate(String... keys) {
|
||||||
Tbl tbl = f.createTbl();
|
Tbl tbl = f.createTbl();
|
||||||
tbl.getContent().add(row("谐波电压检验结果", "基波电压(V)", "57.7000"));
|
tbl.getContent().add(row("谐波电压检验结果", "基波电压(V)", "57.7000"));
|
||||||
tbl.getContent().add(row("总谐波畸变率(%)", "", "", "", ""));
|
tbl.getContent().add(row("总谐波畸变率(%)", "", "standardThd", "", ""));
|
||||||
tbl.getContent().add(row("测点", "标准源输出值", "被检仪器测量值", "误差", "标准限值", "检验结论"));
|
tbl.getContent().add(row("测点", "标准源输出值", "被检仪器测量值", "误差", "标准限值", "检验结论"));
|
||||||
tbl.getContent().add(row("", "", "A", "B", "C", "A", "B", "C", "", "A", "B", "C"));
|
tbl.getContent().add(row("", "", "A", "B", "C", "A", "B", "C", "", "A", "B", "C"));
|
||||||
tbl.getContent().add(row("THD", "", "", "", "", "", "", "", "", "", "", ""));
|
tbl.getContent().add(row("THD", "standardThd", "thdA", "thdB", "thdC", "", "", "", "", "", "", ""));
|
||||||
tbl.getContent().add(row(keys));
|
tbl.getContent().add(row(keys));
|
||||||
JAXBElement<Tbl> el = new JAXBElement<>(new QName(W_NS, "tbl"), Tbl.class, tbl);
|
JAXBElement<Tbl> el = new JAXBElement<>(new QName(W_NS, "tbl"), Tbl.class, tbl);
|
||||||
Docx4jUtil.HeadingContent hc = new Docx4jUtil.HeadingContent();
|
Docx4jUtil.HeadingContent hc = new Docx4jUtil.HeadingContent();
|
||||||
@@ -87,6 +87,33 @@ public class ItemAnchorAssemblerTest {
|
|||||||
return hc;
|
return hc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Docx4jUtil.HeadingContent hpTemplate(String... keys) {
|
||||||
|
Tbl tbl = f.createTbl();
|
||||||
|
tbl.getContent().add(row("谐波功率检验结果", "基波电压(V)", "", "基波电流(A)", ""));
|
||||||
|
tbl.getContent().add(row("谐波次数", "标准源输出值", "被检仪器测量值", "误差", "允许误差", "检定结论"));
|
||||||
|
tbl.getContent().add(row("", "", "L1", "L2", "L3", "L1", "L2", "L3", "", ""));
|
||||||
|
tbl.getContent().add(row(keys));
|
||||||
|
JAXBElement<Tbl> el = new JAXBElement<>(new QName(W_NS, "tbl"), Tbl.class, tbl);
|
||||||
|
Docx4jUtil.HeadingContent hc = new Docx4jUtil.HeadingContent();
|
||||||
|
hc.setHeadingText("HP");
|
||||||
|
hc.addSubContent(el);
|
||||||
|
return hc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Docx4jUtil.HeadingContent hpTemplateWithSplitHeader(String... keys) {
|
||||||
|
Tbl tbl = f.createTbl();
|
||||||
|
tbl.getContent().add(row("谐波功率检验结果", "基波电压(V)", ""));
|
||||||
|
tbl.getContent().add(row("", "基波电流(A)", ""));
|
||||||
|
tbl.getContent().add(row("谐波次数", "标准源输出值", "被检仪器测量值", "误差", "允许误差", "检定结论"));
|
||||||
|
tbl.getContent().add(row("", "", "L1", "L2", "L3", "L1", "L2", "L3", "", ""));
|
||||||
|
tbl.getContent().add(row(keys));
|
||||||
|
JAXBElement<Tbl> el = new JAXBElement<>(new QName(W_NS, "tbl"), Tbl.class, tbl);
|
||||||
|
Docx4jUtil.HeadingContent hc = new Docx4jUtil.HeadingContent();
|
||||||
|
hc.setHeadingText("HP");
|
||||||
|
hc.addSubContent(el);
|
||||||
|
return hc;
|
||||||
|
}
|
||||||
|
|
||||||
private SingleTestResult resultWithRows(String errorScope, List<Map<String, String>> rows) {
|
private SingleTestResult resultWithRows(String errorScope, List<Map<String, String>> rows) {
|
||||||
Map<String, List<Map<String, String>>> byError = new LinkedHashMap<>();
|
Map<String, List<Map<String, String>>> byError = new LinkedHashMap<>();
|
||||||
byError.put(errorScope, rows);
|
byError.put(errorScope, rows);
|
||||||
@@ -146,6 +173,27 @@ public class ItemAnchorAssemblerTest {
|
|||||||
assertEquals("", cellText(tbl, 1, 3)); // 数据侧没有的列留空
|
assertEquals("", cellText(tbl, 1, 3)); // 数据侧没有的列留空
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildTables_harmonicVoltageAndPowerStripErrorScopeUnit() {
|
||||||
|
Docx4jUtil.HeadingContent tpl = template("time", "errorScope");
|
||||||
|
Map<String, String> row = new HashMap<>();
|
||||||
|
row.put("time", "2");
|
||||||
|
SingleTestResult hv = resultWithRows("0.15%", Collections.singletonList(row));
|
||||||
|
SingleTestResult hp = resultWithRows("-0.1~0.1W", Collections.singletonList(row));
|
||||||
|
|
||||||
|
List<Object> hvOut = ItemAnchorAssembler.buildTables("HV", Collections.singletonList(hv), tpl,
|
||||||
|
Arrays.asList("time", "errorScope"), f);
|
||||||
|
List<Object> hpOut = ItemAnchorAssembler.buildTables("HP", Collections.singletonList(hp), tpl,
|
||||||
|
Arrays.asList("time", "errorScope"), f);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tbl hvTbl = ((JAXBElement<Tbl>) hvOut.get(0)).getValue();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tbl hpTbl = ((JAXBElement<Tbl>) hpOut.get(0)).getValue();
|
||||||
|
assertEquals("0.15", cellText(hvTbl, 1, 1));
|
||||||
|
assertEquals("-0.1~0.1", cellText(hpTbl, 1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void buildTables_powerMapping_sumsThreePhases() {
|
public void buildTables_powerMapping_sumsThreePhases() {
|
||||||
Docx4jUtil.HeadingContent tpl = template("standardV", "activePower", "reactivePower");
|
Docx4jUtil.HeadingContent tpl = template("standardV", "activePower", "reactivePower");
|
||||||
@@ -192,28 +240,66 @@ public class ItemAnchorAssemblerTest {
|
|||||||
SingleTestResult empty = new SingleTestResult(); // detail 为 null → 跳过
|
SingleTestResult empty = new SingleTestResult(); // detail 为 null → 跳过
|
||||||
List<Object> out = ItemAnchorAssembler.buildTables("HV", Arrays.asList(r1, empty, r2), tpl,
|
List<Object> out = ItemAnchorAssembler.buildTables("HV", Arrays.asList(r1, empty, r2), tpl,
|
||||||
Arrays.asList("time", "standard"), f);
|
Arrays.asList("time", "standard"), f);
|
||||||
// 空结果跳过 → 2 张表;多表时每张表前有"测点{n}"标题段 → 共 4 个元素
|
// 空结果跳过 → 2 张表;多表之间允许有空段落防止 Word 合并,但不得出现可见的表外"测点"标题
|
||||||
assertEquals(4, out.size());
|
|
||||||
assertEquals(2, countTables(out));
|
assertEquals(2, countTables(out));
|
||||||
|
assertTrue(nonBlankParagraphTexts(out).isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** F1:多表(谐波多 scriptIndex)背靠背插入会被 Word 合并渲染成一张表,相邻表之间必须有段落分隔 */
|
/** HP 多测点:测点号必须写在表格首行标题单元格内,不能生成表外可见标题段 */
|
||||||
@Test
|
@Test
|
||||||
public void buildTables_multipleTables_separatedByPointCaption() {
|
public void buildTables_harmonicPowerMultipleTables_putsPointCaptionInTitleCell() {
|
||||||
Docx4jUtil.HeadingContent tpl = template("time", "standard");
|
List<String> keys = Arrays.asList("time", "standard");
|
||||||
|
Docx4jUtil.HeadingContent tpl = hpTemplate(keys.toArray(new String[0]));
|
||||||
Map<String, String> row = new HashMap<>();
|
Map<String, String> row = new HashMap<>();
|
||||||
row.put("time", "2");
|
row.put("time", "3");
|
||||||
row.put("standard", "0.5000");
|
row.put("standard", "0.5000");
|
||||||
SingleTestResult r1 = resultWithRows("0.05", Collections.singletonList(row));
|
SingleTestResult r1 = resultWithRows("0.05", Collections.singletonList(row));
|
||||||
SingleTestResult r2 = resultWithRows("0.05", Collections.singletonList(row));
|
SingleTestResult r2 = resultWithRows("0.05", Collections.singletonList(row));
|
||||||
List<Object> out = ItemAnchorAssembler.buildTables("HV", Arrays.asList(r1, r2), tpl,
|
List<Object> out = ItemAnchorAssembler.buildTables("HP", Arrays.asList(r1, r2), tpl, keys, f);
|
||||||
Arrays.asList("time", "standard"), f);
|
|
||||||
// 期望序列:测点1标题段、表1、测点2标题段、表2
|
assertEquals(2, countTables(out));
|
||||||
assertEquals(4, out.size());
|
assertTrue(nonBlankParagraphTexts(out).isEmpty());
|
||||||
assertEquals("测点1", captionText(out.get(0)));
|
List<Tbl> tables = collectTables(out);
|
||||||
assertTrue(unwrap(out.get(1)) instanceof Tbl);
|
assertEquals("谐波功率检验结果(测点1)", cellText(tables.get(0), 0, 0));
|
||||||
assertEquals("测点2", captionText(out.get(2)));
|
assertEquals("谐波功率检验结果(测点2)", cellText(tables.get(1), 0, 0));
|
||||||
assertTrue(unwrap(out.get(3)) instanceof Tbl);
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildTables_harmonicPowerFillsBaseVoltageAndCurrentHeaderCells() {
|
||||||
|
List<String> keys = Arrays.asList("time", "standard");
|
||||||
|
Docx4jUtil.HeadingContent tpl = hpTemplate(keys.toArray(new String[0]));
|
||||||
|
Map<String, String> row = new HashMap<>();
|
||||||
|
row.put("time", "3");
|
||||||
|
row.put("standard", "23.0800");
|
||||||
|
row.put("standardV", "57.7000");
|
||||||
|
row.put("standardI", "5.0000");
|
||||||
|
SingleTestResult result = resultWithRows("1.5W", Collections.singletonList(row));
|
||||||
|
|
||||||
|
List<Object> out = ItemAnchorAssembler.buildTables("HP", Collections.singletonList(result), tpl, keys, f);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tbl tbl = ((JAXBElement<Tbl>) out.get(0)).getValue();
|
||||||
|
assertEquals("57.7000", cellText(tbl, 0, 2));
|
||||||
|
assertEquals("5.0000", cellText(tbl, 0, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildTables_harmonicPowerFillsCurrentWhenHeaderIsOnSecondRow() {
|
||||||
|
List<String> keys = Arrays.asList("time", "standard");
|
||||||
|
Docx4jUtil.HeadingContent tpl = hpTemplateWithSplitHeader(keys.toArray(new String[0]));
|
||||||
|
Map<String, String> row = new HashMap<>();
|
||||||
|
row.put("time", "3");
|
||||||
|
row.put("standard", "23.0800");
|
||||||
|
row.put("standardV", "57.7000");
|
||||||
|
row.put("standardI", "5.0000");
|
||||||
|
SingleTestResult result = resultWithRows("1.5W", Collections.singletonList(row));
|
||||||
|
|
||||||
|
List<Object> out = ItemAnchorAssembler.buildTables("HP", Collections.singletonList(result), tpl, keys, f);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tbl tbl = ((JAXBElement<Tbl>) out.get(0)).getValue();
|
||||||
|
assertEquals("57.7000", cellText(tbl, 0, 2));
|
||||||
|
assertEquals("5.0000", cellText(tbl, 1, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** F1 边界:单表不加标题段,既有单表输出不变 */
|
/** F1 边界:单表不加标题段,既有单表输出不变 */
|
||||||
@@ -281,6 +367,182 @@ public class ItemAnchorAssemblerTest {
|
|||||||
assertEquals(expected, ItemAnchorAssembler.extractTableKeys(Collections.singletonList(tpl)));
|
assertEquals(expected, ItemAnchorAssembler.extractTableKeys(Collections.singletonList(tpl)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildTables_harmonicVoltageCalculatesThdFixedRowFromPhaseRates() {
|
||||||
|
List<String> keys = Arrays.asList("time", "standard", "testA", "testB", "testC",
|
||||||
|
"errorA", "errorB", "errorC", "errorScope", "resultA", "resultB", "resultC");
|
||||||
|
Docx4jUtil.HeadingContent tpl = hvTemplate(keys.toArray(new String[0]));
|
||||||
|
Map<String, String> row1 = harmonicRow("2", "3.0000", "6.0000", "8.0000");
|
||||||
|
row1.put("standard", "3.0000");
|
||||||
|
Map<String, String> row2 = harmonicRow("3", "4.0000", "8.0000", "15.0000");
|
||||||
|
row2.put("standard", "4.0000");
|
||||||
|
SingleTestResult r = resultWithRows("0.05", Arrays.asList(row1, row2));
|
||||||
|
|
||||||
|
List<Object> out = ItemAnchorAssembler.buildTables("HV", Collections.singletonList(r), tpl, keys, f);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tbl tbl = ((JAXBElement<Tbl>) out.get(0)).getValue();
|
||||||
|
assertEquals("5.0000", cellText(tbl, 1, 2));
|
||||||
|
assertEquals("5.0000", cellText(tbl, 4, 1));
|
||||||
|
assertEquals("5.0000", cellText(tbl, 4, 2));
|
||||||
|
assertEquals("10.0000", cellText(tbl, 4, 3));
|
||||||
|
assertEquals("17.0000", cellText(tbl, 4, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildTables_harmonicCurrentCalculatesRateRowsAndThdFromBaseCurrent() {
|
||||||
|
List<String> keys = Arrays.asList("time", "standardRate", "testRateA", "testRateB", "testRateC",
|
||||||
|
"errorRateA", "errorRateB", "errorRateC", "errorScope", "resultA", "resultB", "resultC");
|
||||||
|
Docx4jUtil.HeadingContent tpl = hvTemplate(keys.toArray(new String[0]));
|
||||||
|
Map<String, String> row1 = harmonicRow("2", "1.2000", "1.6000", "2.0000");
|
||||||
|
row1.put("standard", "1.0000");
|
||||||
|
row1.put("standardRate", "25.0000");
|
||||||
|
row1.put("errorA", "0.2000");
|
||||||
|
row1.put("errorB", "0.6000");
|
||||||
|
row1.put("errorC", "1.0000");
|
||||||
|
row1.put("baseCurrentA", "4.0000");
|
||||||
|
row1.put("baseCurrentB", "4.0000");
|
||||||
|
row1.put("baseCurrentC", "4.0000");
|
||||||
|
Map<String, String> row2 = harmonicRow("3", "0.9000", "1.2000", "1.5000");
|
||||||
|
row2.put("standard", "1.0000");
|
||||||
|
row2.put("standardRate", "25.0000");
|
||||||
|
row2.put("errorA", "-0.1000");
|
||||||
|
row2.put("errorB", "0.2000");
|
||||||
|
row2.put("errorC", "0.5000");
|
||||||
|
row2.put("baseCurrentA", "4.0000");
|
||||||
|
row2.put("baseCurrentB", "4.0000");
|
||||||
|
row2.put("baseCurrentC", "4.0000");
|
||||||
|
SingleTestResult r = resultWithRows("0.05", Arrays.asList(row1, row2));
|
||||||
|
|
||||||
|
List<Object> out = ItemAnchorAssembler.buildTables("HI", Collections.singletonList(r), tpl, keys, f);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tbl tbl = ((JAXBElement<Tbl>) out.get(0)).getValue();
|
||||||
|
assertEquals("35.3553", cellText(tbl, 1, 2));
|
||||||
|
assertEquals("35.3553", cellText(tbl, 4, 1));
|
||||||
|
assertEquals("37.5000", cellText(tbl, 4, 2));
|
||||||
|
assertEquals("50.0000", cellText(tbl, 4, 3));
|
||||||
|
assertEquals("62.5000", cellText(tbl, 4, 4));
|
||||||
|
assertEquals("25.0000", cellText(tbl, 5, 1));
|
||||||
|
assertEquals("30.0000", cellText(tbl, 5, 2));
|
||||||
|
assertEquals("40.0000", cellText(tbl, 5, 3));
|
||||||
|
assertEquals("50.0000", cellText(tbl, 5, 4));
|
||||||
|
assertEquals("5.0000", cellText(tbl, 5, 5));
|
||||||
|
assertEquals("15.0000", cellText(tbl, 5, 6));
|
||||||
|
assertEquals("25.0000", cellText(tbl, 5, 7));
|
||||||
|
assertEquals("±1.2500", cellText(tbl, 5, 8));
|
||||||
|
assertEquals("±1.2500", cellText(tbl, 6, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildTables_harmonicCurrentLeavesStandardRateBlankWhenScriptRateMissing() {
|
||||||
|
List<String> keys = Arrays.asList("time", "standardRate", "testRateA", "testRateB", "testRateC",
|
||||||
|
"errorRateA", "errorRateB", "errorRateC", "errorScope", "resultA", "resultB", "resultC");
|
||||||
|
Docx4jUtil.HeadingContent tpl = hvTemplate(keys.toArray(new String[0]));
|
||||||
|
Map<String, String> row = harmonicRow("2", "1.0000", "1.0000", "1.0000");
|
||||||
|
row.put("standard", "1.0000");
|
||||||
|
row.put("baseCurrentA", "5.0000");
|
||||||
|
row.put("baseCurrentB", "5.0000");
|
||||||
|
row.put("baseCurrentC", "5.0000");
|
||||||
|
SingleTestResult r = resultWithRows("0.05", Collections.singletonList(row));
|
||||||
|
|
||||||
|
List<Object> out = ItemAnchorAssembler.buildTables("HI", Collections.singletonList(r), tpl, keys, f);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tbl tbl = ((JAXBElement<Tbl>) out.get(0)).getValue();
|
||||||
|
assertEquals("", cellText(tbl, 1, 2));
|
||||||
|
assertEquals("", cellText(tbl, 4, 1));
|
||||||
|
assertEquals("", cellText(tbl, 5, 1));
|
||||||
|
assertEquals("20.0000", cellText(tbl, 5, 2));
|
||||||
|
assertEquals("", cellText(tbl, 5, 5));
|
||||||
|
assertEquals("", cellText(tbl, 5, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildTables_harmonicCurrentKeepsScriptStandardRateWhenAmplitudeAvailable() {
|
||||||
|
List<String> keys = Arrays.asList("time", "standardRate", "testRateA", "testRateB", "testRateC",
|
||||||
|
"errorRateA", "errorRateB", "errorRateC", "errorScope", "resultA", "resultB", "resultC");
|
||||||
|
Docx4jUtil.HeadingContent tpl = hvTemplate(keys.toArray(new String[0]));
|
||||||
|
Map<String, String> row = harmonicRow("2", "1.0000", "1.0000", "1.0000");
|
||||||
|
row.put("standard", "2.0000");
|
||||||
|
row.put("standardRate", "1.0000");
|
||||||
|
row.put("baseCurrentA", "10.0000");
|
||||||
|
row.put("baseCurrentB", "10.0000");
|
||||||
|
row.put("baseCurrentC", "10.0000");
|
||||||
|
SingleTestResult r = resultWithRows("0.05", Collections.singletonList(row));
|
||||||
|
|
||||||
|
List<Object> out = ItemAnchorAssembler.buildTables("HI", Collections.singletonList(r), tpl, keys, f);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tbl tbl = ((JAXBElement<Tbl>) out.get(0)).getValue();
|
||||||
|
assertEquals("1.0000", cellText(tbl, 5, 1));
|
||||||
|
assertEquals("9.0000", cellText(tbl, 5, 5));
|
||||||
|
assertEquals("±0.1500", cellText(tbl, 5, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildTables_harmonicCurrentErrorRateUsesRateDifference() {
|
||||||
|
List<String> keys = Arrays.asList("time", "standardRate", "testRateA", "testRateB", "testRateC",
|
||||||
|
"errorRateA", "errorRateB", "errorRateC", "errorScope", "resultA", "resultB", "resultC");
|
||||||
|
Docx4jUtil.HeadingContent tpl = hvTemplate(keys.toArray(new String[0]));
|
||||||
|
Map<String, String> row = harmonicRow("2", "2.0000", "1.0000", "1.0000");
|
||||||
|
row.put("standard", "1.0000");
|
||||||
|
row.put("standardRate", "10.0000");
|
||||||
|
row.put("errorA", "0.0100");
|
||||||
|
row.put("baseCurrentA", "10.0000");
|
||||||
|
row.put("baseCurrentB", "10.0000");
|
||||||
|
row.put("baseCurrentC", "10.0000");
|
||||||
|
SingleTestResult r = resultWithRows("0.05", Collections.singletonList(row));
|
||||||
|
|
||||||
|
List<Object> out = ItemAnchorAssembler.buildTables("HI", Collections.singletonList(r), tpl, keys, f);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tbl tbl = ((JAXBElement<Tbl>) out.get(0)).getValue();
|
||||||
|
assertEquals("10.0000", cellText(tbl, 5, 1));
|
||||||
|
assertEquals("20.0000", cellText(tbl, 5, 2));
|
||||||
|
assertEquals("10.0000", cellText(tbl, 5, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildTables_harmonicCurrentKeepsScriptStandardRateWhenAmplitudeMissing() {
|
||||||
|
List<String> keys = Arrays.asList("time", "standardRate", "testRateA", "testRateB", "testRateC",
|
||||||
|
"errorRateA", "errorRateB", "errorRateC", "errorScope", "resultA", "resultB", "resultC");
|
||||||
|
Docx4jUtil.HeadingContent tpl = hvTemplate(keys.toArray(new String[0]));
|
||||||
|
Map<String, String> row = harmonicRow("2", "0.040144", "0.040248", "0.040116");
|
||||||
|
row.put("standard", "/");
|
||||||
|
row.put("standardRate", "1.0000");
|
||||||
|
row.put("baseCurrentA", "4.0000");
|
||||||
|
row.put("baseCurrentB", "4.0000");
|
||||||
|
row.put("baseCurrentC", "4.0000");
|
||||||
|
SingleTestResult r = resultWithRows("0.0075A", Collections.singletonList(row));
|
||||||
|
|
||||||
|
List<Object> out = ItemAnchorAssembler.buildTables("HI", Collections.singletonList(r), tpl, keys, f);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Tbl tbl = ((JAXBElement<Tbl>) out.get(0)).getValue();
|
||||||
|
assertEquals("1.0000", cellText(tbl, 1, 2));
|
||||||
|
assertEquals("1.0000", cellText(tbl, 4, 1));
|
||||||
|
assertEquals("1.0000", cellText(tbl, 5, 1));
|
||||||
|
assertEquals("1.0036", cellText(tbl, 5, 2));
|
||||||
|
assertEquals("0.0036", cellText(tbl, 5, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> harmonicRow(String time, String testA, String testB, String testC) {
|
||||||
|
Map<String, String> row = new HashMap<>();
|
||||||
|
row.put("time", time);
|
||||||
|
row.put("standard", "0.0000");
|
||||||
|
row.put("testA", testA);
|
||||||
|
row.put("testB", testB);
|
||||||
|
row.put("testC", testC);
|
||||||
|
row.put("errorA", "0.0000");
|
||||||
|
row.put("errorB", "0.0000");
|
||||||
|
row.put("errorC", "0.0000");
|
||||||
|
row.put("resultA", "合格");
|
||||||
|
row.put("resultB", "合格");
|
||||||
|
row.put("resultC", "合格");
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
private Object unwrap(Object o) {
|
private Object unwrap(Object o) {
|
||||||
return (o instanceof JAXBElement) ? ((JAXBElement<?>) o).getValue() : o;
|
return (o instanceof JAXBElement) ? ((JAXBElement<?>) o).getValue() : o;
|
||||||
}
|
}
|
||||||
@@ -295,10 +557,29 @@ public class ItemAnchorAssemblerTest {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String captionText(Object o) {
|
private List<Tbl> collectTables(List<Object> out) {
|
||||||
|
List<Tbl> tables = new ArrayList<>();
|
||||||
|
for (Object o : out) {
|
||||||
Object v = unwrap(o);
|
Object v = unwrap(o);
|
||||||
assertTrue("相邻表之间应为段落,实际是 " + v.getClass().getSimpleName(), v instanceof P);
|
if (v instanceof Tbl) {
|
||||||
return Docx4jUtil.getTextFromP((P) v);
|
tables.add((Tbl) v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> nonBlankParagraphTexts(List<Object> out) {
|
||||||
|
List<String> texts = new ArrayList<>();
|
||||||
|
for (Object o : out) {
|
||||||
|
Object v = unwrap(o);
|
||||||
|
if (v instanceof P) {
|
||||||
|
String text = Docx4jUtil.getTextFromP((P) v);
|
||||||
|
if (text != null && !text.trim().isEmpty()) {
|
||||||
|
texts.add(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return texts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Tr> collectTrs(Tbl tbl) {
|
private List<Tr> collectTrs(Tbl tbl) {
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
package com.njcn.gather.result.service.impl;
|
||||||
|
|
||||||
|
import com.njcn.gather.report.pojo.enums.ItemReportKeyEnum;
|
||||||
|
import com.njcn.gather.report.pojo.result.SingleTestResult;
|
||||||
|
import com.njcn.gather.script.pojo.po.PqScriptCheckData;
|
||||||
|
import com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO;
|
||||||
|
import com.njcn.gather.script.service.IPqScriptCheckDataService;
|
||||||
|
import com.njcn.gather.storage.pojo.po.SimAndDigHarmonicResult;
|
||||||
|
import com.njcn.gather.storage.pojo.po.SimAndDigNonHarmonicResult;
|
||||||
|
import com.njcn.gather.storage.pojo.param.SingleNonHarmParam;
|
||||||
|
import com.njcn.gather.storage.service.SimAndDigHarmonicService;
|
||||||
|
import com.njcn.gather.storage.service.SimAndDigNonHarmonicService;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyList;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class ResultServiceImplTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fillThreePhaseData_harmonicCurrentCarriesBaseCurrentForRateCalculation() throws Exception {
|
||||||
|
SimAndDigHarmonicResult result = new SimAndDigHarmonicResult();
|
||||||
|
result.setAValue1(detectionData("4.0000", "10.0000"));
|
||||||
|
result.setBValue1(detectionData("5.0000", "11.0000"));
|
||||||
|
result.setCValue1(detectionData("6.0000", "12.0000"));
|
||||||
|
result.setAValue2(detectionData("1.0000"));
|
||||||
|
result.setBValue2(detectionData("1.5000"));
|
||||||
|
result.setCValue2(detectionData("2.0000"));
|
||||||
|
Map<String, String> row = new HashMap<>();
|
||||||
|
|
||||||
|
Method method = ResultServiceImpl.class.getDeclaredMethod(
|
||||||
|
"fillThreePhaseData", Object.class, Integer.class, Map.class, String.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
|
||||||
|
boolean filled = (Boolean) method.invoke(newResultService(), result, 2, row, "HI");
|
||||||
|
|
||||||
|
assertTrue(filled);
|
||||||
|
assertEquals("4.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_A.getKey()));
|
||||||
|
assertEquals("5.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_B.getKey()));
|
||||||
|
assertEquals("6.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_C.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fillThreePhaseData_harmonicCurrentUsesBaseCurrentDataWhenResultDataMissing() throws Exception {
|
||||||
|
SimAndDigHarmonicResult result = new SimAndDigHarmonicResult();
|
||||||
|
result.setAValue1(detectionDataOnlyData("1.0000"));
|
||||||
|
result.setBValue1(detectionDataOnlyData("2.0000"));
|
||||||
|
result.setCValue1(detectionDataOnlyData("3.0000"));
|
||||||
|
result.setAValue2(detectionData("0.1000"));
|
||||||
|
result.setBValue2(detectionData("0.2000"));
|
||||||
|
result.setCValue2(detectionData("0.3000"));
|
||||||
|
Map<String, String> row = new HashMap<>();
|
||||||
|
|
||||||
|
Method method = ResultServiceImpl.class.getDeclaredMethod(
|
||||||
|
"fillThreePhaseData", Object.class, Integer.class, Map.class, String.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
|
||||||
|
boolean filled = (Boolean) method.invoke(newResultService(), result, 2, row, "HI");
|
||||||
|
|
||||||
|
assertTrue(filled);
|
||||||
|
assertEquals("1.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_A.getKey()));
|
||||||
|
assertEquals("2.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_B.getKey()));
|
||||||
|
assertEquals("3.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_C.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fillHarmonicScriptStandard_harmonicCurrentCopiesScriptPercentToStandardRate() throws Exception {
|
||||||
|
Map<String, String> row = new HashMap<>();
|
||||||
|
List<PqScriptCheckData> checkData = Arrays.asList(
|
||||||
|
checkData("A", 2D, 1D),
|
||||||
|
checkData("B", 2D, 1D),
|
||||||
|
checkData("C", 2D, 1D)
|
||||||
|
);
|
||||||
|
|
||||||
|
Method method = ResultServiceImpl.class.getDeclaredMethod(
|
||||||
|
"fillHarmonicScriptStandard", Map.class, String.class, List.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
|
||||||
|
method.invoke(newResultService(), row, "HI", checkData);
|
||||||
|
|
||||||
|
assertEquals("1.0000", row.get(ItemReportKeyEnum.STANDARD_RATE.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFinalContent_harmonicPowerCarriesBaseVoltageAndCurrentFromScriptDetails() throws Exception {
|
||||||
|
IPqScriptCheckDataService checkDataService = mock(IPqScriptCheckDataService.class);
|
||||||
|
SimAndDigHarmonicService harmonicService = mock(SimAndDigHarmonicService.class);
|
||||||
|
when(checkDataService.listCheckData(eq("script"), anyList()))
|
||||||
|
.thenReturn(Collections.singletonList(checkData("A", 3D, 23.08D)));
|
||||||
|
SimAndDigHarmonicResult harmonicResult = new SimAndDigHarmonicResult();
|
||||||
|
harmonicResult.setAValue3(detectionData("23.0745", "23.0800"));
|
||||||
|
harmonicResult.setBValue3(detectionData("23.0809", "23.0800"));
|
||||||
|
harmonicResult.setCValue3(detectionData("23.0814", "23.0800"));
|
||||||
|
when(harmonicService.getSingleResult(any())).thenReturn(harmonicResult);
|
||||||
|
List<PqScriptDtlDataVO> scriptDetails = Arrays.asList(
|
||||||
|
scriptDetail("VOL", "A", 57.7D),
|
||||||
|
scriptDetail("CUR", "A", 5.0D)
|
||||||
|
);
|
||||||
|
|
||||||
|
SingleTestResult result = newResultService(harmonicService, checkDataService)
|
||||||
|
.getFinalContent(scriptDetails, "plan", "dev", 1,
|
||||||
|
Arrays.asList("time", "standardV", "standardI"));
|
||||||
|
|
||||||
|
Map<String, String> row = firstRow(result);
|
||||||
|
assertEquals("57.7000", row.get(ItemReportKeyEnum.STANDARD_V.getKey()));
|
||||||
|
assertEquals("5.0000", row.get(ItemReportKeyEnum.STANDARD_I.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFinalContent_angleUsesCurrentAngleResultRows() throws Exception {
|
||||||
|
IPqScriptCheckDataService checkDataService = mock(IPqScriptCheckDataService.class);
|
||||||
|
SimAndDigNonHarmonicService nonHarmonicService = mock(SimAndDigNonHarmonicService.class);
|
||||||
|
when(checkDataService.listCheckData(eq("script"), anyList())).thenReturn(Collections.emptyList());
|
||||||
|
SimAndDigNonHarmonicResult result = new SimAndDigNonHarmonicResult();
|
||||||
|
result.setSort(21);
|
||||||
|
result.setAdType("I1A");
|
||||||
|
result.setAValue(detectionAngleData("61.4", "60", "1.4", 2));
|
||||||
|
result.setBValue(detectionAngleData("61", "60", "1", 1));
|
||||||
|
result.setCValue(detectionAngleData("61", "60", "1", 1));
|
||||||
|
when(nonHarmonicService.queryBySortListWithAdTypeCode(any(SingleNonHarmParam.class)))
|
||||||
|
.thenReturn(Collections.singletonList(result));
|
||||||
|
|
||||||
|
SingleTestResult reportResult = newResultService(null, checkDataService, nonHarmonicService)
|
||||||
|
.getFinalContent(Collections.singletonList(scriptDetail("Angle", "Base", 21)),
|
||||||
|
"plan", "dev", 1, Arrays.asList("standard", "testA", "testB", "testC"));
|
||||||
|
|
||||||
|
Map<String, String> row = firstRow(reportResult);
|
||||||
|
assertEquals("60.0000", row.get(ItemReportKeyEnum.STANDARD.getKey()));
|
||||||
|
assertEquals("61.4000", row.get(ItemReportKeyEnum.TEST_A.getKey()));
|
||||||
|
assertEquals("61.0000", row.get(ItemReportKeyEnum.TEST_B.getKey()));
|
||||||
|
assertEquals("61.0000", row.get(ItemReportKeyEnum.TEST_C.getKey()));
|
||||||
|
assertEquals("不合格", row.get(ItemReportKeyEnum.RESULT_A.getKey()));
|
||||||
|
assertEquals("合格", row.get(ItemReportKeyEnum.RESULT_B.getKey()));
|
||||||
|
assertEquals("合格", row.get(ItemReportKeyEnum.RESULT_C.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultServiceImpl newResultService() throws Exception {
|
||||||
|
return newResultService(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultServiceImpl newResultService(SimAndDigHarmonicService harmonicService,
|
||||||
|
IPqScriptCheckDataService checkDataService) throws Exception {
|
||||||
|
return newResultService(harmonicService, checkDataService, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultServiceImpl newResultService(SimAndDigHarmonicService harmonicService,
|
||||||
|
IPqScriptCheckDataService checkDataService,
|
||||||
|
SimAndDigNonHarmonicService nonHarmonicService) throws Exception {
|
||||||
|
Constructor<?> constructor = ResultServiceImpl.class.getDeclaredConstructors()[0];
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||||
|
Object[] args = new Object[parameterTypes.length];
|
||||||
|
for (int i = 0; i < parameterTypes.length; i++) {
|
||||||
|
if (nonHarmonicService != null && parameterTypes[i].equals(SimAndDigNonHarmonicService.class)) {
|
||||||
|
args[i] = nonHarmonicService;
|
||||||
|
} else if (harmonicService != null && parameterTypes[i].equals(SimAndDigHarmonicService.class)) {
|
||||||
|
args[i] = harmonicService;
|
||||||
|
} else if (checkDataService != null && parameterTypes[i].equals(IPqScriptCheckDataService.class)) {
|
||||||
|
args[i] = checkDataService;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (ResultServiceImpl) constructor.newInstance(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> firstRow(SingleTestResult result) {
|
||||||
|
return result.getDetail().values().iterator().next()
|
||||||
|
.get(0).values().iterator().next()
|
||||||
|
.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String detectionData(String value) {
|
||||||
|
return detectionData(value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String detectionData(String data, String resultData) {
|
||||||
|
return "{\"isData\":1,\"data\":" + data + ",\"resultData\":" + resultData
|
||||||
|
+ ",\"radius\":\"0.05\",\"unit\":\"A\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String detectionDataOnlyData(String data) {
|
||||||
|
return "{\"num\":1,\"isData\":4,\"data\":" + data + ",\"radius\":\"0.05\",\"unit\":\"A\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String detectionAngleData(String data, String resultData, String errorData, int isData) {
|
||||||
|
return "{\"isData\":" + isData + ",\"data\":" + data + ",\"resultData\":" + resultData
|
||||||
|
+ ",\"errorData\":" + errorData + ",\"radius\":\"-1.0~1.0\",\"unit\":\"\\u00b0\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private PqScriptCheckData checkData(String phase, Double harmNum, Double value) {
|
||||||
|
PqScriptCheckData data = new PqScriptCheckData();
|
||||||
|
data.setScriptIndex(3);
|
||||||
|
data.setValueType("HP");
|
||||||
|
data.setPhase(phase);
|
||||||
|
data.setHarmNum(harmNum);
|
||||||
|
data.setValue(value);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PqScriptDtlDataVO scriptDetail(String valueType, String phase, Double value) {
|
||||||
|
PqScriptDtlDataVO detail = new PqScriptDtlDataVO();
|
||||||
|
detail.setScriptId("script");
|
||||||
|
detail.setScriptCode("HP");
|
||||||
|
detail.setScriptSubType("Base");
|
||||||
|
detail.setScriptIndex(3);
|
||||||
|
detail.setValueType(valueType);
|
||||||
|
detail.setPhase(phase);
|
||||||
|
detail.setValue(value);
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PqScriptDtlDataVO scriptDetail(String scriptCode, String scriptSubType, Integer scriptIndex) {
|
||||||
|
PqScriptDtlDataVO detail = new PqScriptDtlDataVO();
|
||||||
|
detail.setScriptId("script");
|
||||||
|
detail.setScriptCode(scriptCode);
|
||||||
|
detail.setScriptSubType(scriptSubType);
|
||||||
|
detail.setScriptIndex(scriptIndex);
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.njcn.gather.result.util;
|
||||||
|
|
||||||
|
import com.njcn.gather.report.pojo.enums.ItemReportKeyEnum;
|
||||||
|
import com.njcn.gather.storage.pojo.po.SimAndDigNonHarmonicResult;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class AngleResultAssemblerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void assembleRows_usesCurrentAngleI1AAndIgnoresVoltageAngleU1A() {
|
||||||
|
SimAndDigNonHarmonicResult voltageAngle = result("U1A",
|
||||||
|
data(0D, 0D, 0D, 1), data(0D, 0D, 0D, 1), data(0D, 0D, 0D, 1));
|
||||||
|
SimAndDigNonHarmonicResult currentAngle = result("I1A",
|
||||||
|
data(61.4D, 60D, 1.4D, 2), data(61D, 60D, 1D, 1), data(61D, 60D, 1D, 1));
|
||||||
|
|
||||||
|
List<Map<String, String>> rows = AngleResultAssembler.assembleRows(Arrays.asList(voltageAngle, currentAngle));
|
||||||
|
|
||||||
|
assertEquals(1, rows.size());
|
||||||
|
Map<String, String> row = rows.get(0);
|
||||||
|
assertEquals("60.0000", row.get(ItemReportKeyEnum.STANDARD.getKey()));
|
||||||
|
assertEquals("61.4000", row.get(ItemReportKeyEnum.TEST_A.getKey()));
|
||||||
|
assertEquals("61.0000", row.get(ItemReportKeyEnum.TEST_B.getKey()));
|
||||||
|
assertEquals("61.0000", row.get(ItemReportKeyEnum.TEST_C.getKey()));
|
||||||
|
assertEquals("1.4000", row.get(ItemReportKeyEnum.ERROR_A.getKey()));
|
||||||
|
assertEquals("1.0000", row.get(ItemReportKeyEnum.ERROR_B.getKey()));
|
||||||
|
assertEquals("1.0000", row.get(ItemReportKeyEnum.ERROR_C.getKey()));
|
||||||
|
assertEquals("不合格", row.get(ItemReportKeyEnum.RESULT_A.getKey()));
|
||||||
|
assertEquals("合格", row.get(ItemReportKeyEnum.RESULT_B.getKey()));
|
||||||
|
assertEquals("合格", row.get(ItemReportKeyEnum.RESULT_C.getKey()));
|
||||||
|
assertEquals("\u00b11.0\u00b0", row.get(ItemReportKeyEnum.ERROR_SCOPE.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void assembleRows_voltageAngleOnlyReturnsNoRows() {
|
||||||
|
SimAndDigNonHarmonicResult voltageAngle = result("U1A",
|
||||||
|
data(0D, 0D, 0D, 1), data(0D, 0D, 0D, 1), data(0D, 0D, 0D, 1));
|
||||||
|
|
||||||
|
assertTrue(AngleResultAssembler.assembleRows(Collections.singletonList(voltageAngle)).isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private SimAndDigNonHarmonicResult result(String adType, String a, String b, String c) {
|
||||||
|
SimAndDigNonHarmonicResult result = new SimAndDigNonHarmonicResult();
|
||||||
|
result.setSort(21);
|
||||||
|
result.setAdType(adType);
|
||||||
|
result.setAValue(a);
|
||||||
|
result.setBValue(b);
|
||||||
|
result.setCValue(c);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String data(Double data, Double resultData, Double errorData, Integer isData) {
|
||||||
|
return "{\"data\":" + data + ",\"unit\":\"\\u00b0\",\"isData\":" + isData
|
||||||
|
+ ",\"radius\":\"-1.0~1.0\",\"errorData\":" + errorData
|
||||||
|
+ ",\"resultData\":" + resultData + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
public class UnbalanceResultAssemblerTest {
|
public class UnbalanceResultAssemblerTest {
|
||||||
|
|
||||||
@@ -72,6 +73,17 @@ public class UnbalanceResultAssemblerTest {
|
|||||||
assertEquals("合格", row.get(ItemReportKeyEnum.RESULT.getKey()));
|
assertEquals("合格", row.get(ItemReportKeyEnum.RESULT.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void assembleRows_leavesTimeUnsetForItemAnchorPointFallback() {
|
||||||
|
SimAndDigNonHarmonicResult unbalance = total("V_UNBAN", 7,
|
||||||
|
"{\"data\": 0.05, \"unit\": \"%\", \"isData\": 1, \"radius\": \"-0.2~0.2\", \"errorData\": 0.01, \"resultData\": 0.04}");
|
||||||
|
|
||||||
|
List<Map<String, String>> rows = UnbalanceResultAssembler.assembleRows(Arrays.asList(unbalance));
|
||||||
|
|
||||||
|
assertEquals(1, rows.size());
|
||||||
|
assertNull(rows.get(0).get(ItemReportKeyEnum.TIME.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
private SimAndDigNonHarmonicResult total(String code, Integer sort, String tValue) {
|
private SimAndDigNonHarmonicResult total(String code, Integer sort, String tValue) {
|
||||||
SimAndDigNonHarmonicResult result = new SimAndDigNonHarmonicResult();
|
SimAndDigNonHarmonicResult result = new SimAndDigNonHarmonicResult();
|
||||||
result.setAdType(code);
|
result.setAdType(code);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import lombok.Setter;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.docx4j.XmlUtils;
|
import org.docx4j.XmlUtils;
|
||||||
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||||
|
import org.docx4j.openpackaging.parts.WordprocessingML.DocumentSettingsPart;
|
||||||
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
|
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
|
||||||
import org.docx4j.wml.*;
|
import org.docx4j.wml.*;
|
||||||
import org.docx4j.wml.TcPrInner.GridSpan;
|
import org.docx4j.wml.TcPrInner.GridSpan;
|
||||||
@@ -31,6 +32,27 @@ import java.util.Map;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class Docx4jUtil {
|
public class Docx4jUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 让 Word 打开文档时自动刷新 PAGE / NUMPAGES 等域,避免动态插表后页码显示旧值。
|
||||||
|
*/
|
||||||
|
public static void enableUpdateFieldsOnOpen(WordprocessingMLPackage wordPackage) throws Exception {
|
||||||
|
if (wordPackage == null || wordPackage.getMainDocumentPart() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DocumentSettingsPart settingsPart = wordPackage.getMainDocumentPart().getDocumentSettingsPart(true);
|
||||||
|
CTSettings settings = settingsPart.getContents();
|
||||||
|
if (settings == null) {
|
||||||
|
settings = new CTSettings();
|
||||||
|
settingsPart.setJaxbElement(settings);
|
||||||
|
}
|
||||||
|
BooleanDefaultTrue updateFields = settings.getUpdateFields();
|
||||||
|
if (updateFields == null) {
|
||||||
|
updateFields = new BooleanDefaultTrue();
|
||||||
|
settings.setUpdateFields(updateFields);
|
||||||
|
}
|
||||||
|
updateFields.setVal(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建标题
|
* 创建标题
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user