浙江报告与日志功能

This commit is contained in:
2025-04-11 11:03:16 +08:00
parent 6727dee61e
commit 6f890daad6
50 changed files with 2703 additions and 544 deletions

View File

@@ -74,6 +74,29 @@
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.docx4j/docx4j -->
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.0</version> <!-- 您可以根据需要选择其他版本 -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.0</version>
</dependency>
</dependencies>

View File

@@ -20,7 +20,7 @@ public class DetectionData {
private Double num;
/**
* 是否是符合数据(1.合格 2.不合格 3.网络超时 4.无法处理 5.不参与误差比较)
* 1.合格 2.不合格 3.网络超时 4.无法处理 5.不参与误差比较
*/
private Integer isData;

View File

@@ -43,6 +43,7 @@ import com.njcn.gather.system.dictionary.service.IDictDataService;
import com.njcn.gather.system.dictionary.service.IDictTypeService;
import com.njcn.gather.type.pojo.po.DevType;
import com.njcn.gather.type.service.IDevTypeService;
import com.njcn.gather.user.user.service.ISysUserService;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.utils.ExcelUtil;
import com.njcn.web.utils.PoiUtil;
@@ -75,6 +76,7 @@ public class PqDevServiceImpl extends ServiceImpl<PqDevMapper, PqDev> implements
private final IDevTypeService devTypeService;
private final ISysTestConfigService sysTestConfigService;
private final IDictTypeService dictTypeService;
private final ISysUserService userService;
@Override
public Page<PqDevVO> listPqDevs(PqDevParam.QueryParam queryParam) {
@@ -390,6 +392,7 @@ public class PqDevServiceImpl extends ServiceImpl<PqDevMapper, PqDev> implements
return CheckStateEnum.UNCHECKED.getValue();
}
//
// @Override
// public List getPieData(String planId) {
// List<PqDev> pqDevList = this.lambdaQuery().eq(PqDev::getPlanId, planId).eq(PqDev::getState, DataStateEnum.ENABLE.getCode()).list();
@@ -411,6 +414,9 @@ public class PqDevServiceImpl extends ServiceImpl<PqDevMapper, PqDev> implements
@Override
public PqDevVO getPqDevById(String id) {
PqDev pqDev = this.getById(id);
if(StrUtil.isNotBlank(pqDev.getCheckBy())){
pqDev.setCheckBy(userService.getById(pqDev.getCheckBy()).getName());
}
PqDevVO pqDevVO = new PqDevVO();
BeanUtil.copyProperties(pqDev, pqDevVO);

View File

@@ -0,0 +1,77 @@
package com.njcn.gather.report.pojo.constant;
import java.util.Arrays;
import java.util.List;
/**
* 电能质量指标常用的一些常量池
*
* @author hongawen
* @version 1.0
* @data 2025/3/27 11:11
*/
public interface PowerConstant {
/**
* 有三相的指标
*/
List<String> THREE_PHASE = Arrays.asList("V", "HV", "HI", "HP", "HSV", "HSI", "I", "P", "F");
/**
* T相指标
*/
List<String> T_PHASE = Arrays.asList("VOLTAGE", "IMBV", "IMBA");
/**
* 有次数的指标
*/
List<String> TIME = Arrays.asList("HV", "HI", "HP", "HSV", "HSI");
/**
* 没有次数的指标
*/
List<String> NO_TIME = Arrays.asList("V", "I", "P", "VOLTAGE", "IMBV", "IMBA", "F");
/**
* 有数据范围
*/
List<Integer> DATA_RANGE = Arrays.asList(1, 2);
/**
* 暂态符号
*/
String VOLTAGE = "VOLTAGE";
/**
* A相
*/
String PHASE_A = "A";
/**
* B相
*/
String PHASE_B = "B";
/**
* C相
*/
String PHASE_C = "C";
/**
* T相
*/
String PHASE_T = "T";
/**
* 电流单位
*/
String CURRENT_UNIT = "A";
/**
* 电压单位
*/
String VOLTAGE_UNIT = "V";
}

View File

@@ -0,0 +1,22 @@
package com.njcn.gather.report.pojo.constant;
/**
*
* 报告相关的一些常量
*
* @author hongawen
* @version 1.0
* @data 2025/4/3 13:44
*/
public interface ReportConstant {
/**
* docx文档后缀
*/
String DOCX = ".docx";
/**
* 报告模板中书签的起始标识
*/
String BOOKMARK_START = "#{";
}

View File

@@ -0,0 +1,43 @@
package com.njcn.gather.report.pojo.enums;
import lombok.Getter;
/**
* 影响量枚举
* @author hongawen
* @version 1.0
* @data 2025/3/27 21:07
*/
@Getter
public enum AffectEnum {
BASE("base", "额定工作条件下的检测"),
VOL("vol", "电压对XX测量的影响"),
FREQ("freq", "频率对XX测量的影响"),
HARM("single", "谐波对XX测量的影响");
private String key;
private String desc;
AffectEnum(String key, String desc) {
this.key = key;
this.desc = desc;
}
/**
* 根据key找到适配的枚举
*
* @param key 枚举的key
* @return 匹配的枚举实例如果没有找到则返回null
*/
public static AffectEnum getByKey(String key) {
for (AffectEnum affectEnum : AffectEnum.values()) {
if (affectEnum.getKey().equalsIgnoreCase(key)) {
return affectEnum;
}
}
return null;
}
}

View File

@@ -0,0 +1,42 @@
package com.njcn.gather.report.pojo.enums;
import lombok.Getter;
/**
*
* 本枚举用于记录模板中关键字的含义
* 比如 ${manufacturer} 对应的设备厂家
*
* @author hongawen
* @version 1.0
* @data 2025/3/24 14:34
*
* */
@Getter
public enum BaseReportKeyEnum {
DEV_TYPE("devType","设备型号、规格"),
DEV_CODE("createId","装置编号"),
POWER("power","工作电源"),
DEV_CURR("devCurr","额定电流"),
DEV_VOLT("devVolt","额定电压"),
COUNT("count","通道数"),
MANUFACTURER("manufacturer","设备厂家、制造厂商"),
SAMPLE_ID("sampleId","样品编号"),
ARRIVED_DATE("arrivedDate","收样日期"),
TEST_DATE("testDate","检测日期"),
INSPECTOR("inspector","检测员"),
YEAR("year","年份"),
MONTH("month","月份"),
DAY("day",""),
YEAR_MONTH_DAY("year-month-day","年-月-日");
private String key;
private String desc;
BaseReportKeyEnum(String key, String desc) {
this.key = key;
this.desc = desc;
}
}

View File

@@ -0,0 +1,50 @@
package com.njcn.gather.report.pojo.enums;
import lombok.Getter;
/**
* 统计文档锚点类型
*
* @author hongawen
* @version 1.0
* @data 2025/3/24 18:42
*/
@Getter
public enum DocAnchorEnum {
DATA_LINE("#{data:line}", "准确度数据展示区域,以测试回路维度展示", 1),
DATA_SCRIPT("#{data:script}", "准确度数据展示区域,以检测项维度展示", 1),
TEST_RESULT_DEV("#{testResult:dev}", "检测结论,仅有设备结论", 2),
TEST_RESULT_LINE("#{testResult:line}", "检测结论,仅有回路结论", 2),
TEST_RESULT_DETAIL("#{testResult:detail}", "检测结论,包含回路、设备结论", 2),
CATALOG("#{catalog}", "目录信息", 3);
private String key;
private String desc;
private Integer sort;
DocAnchorEnum(String key, String desc, Integer sort) {
this.key = key;
this.desc = desc;
this.sort = sort;
}
/**
* 根据key找到适配的枚举
*
* @param key 枚举的key
* @return 匹配的枚举实例如果没有找到则返回null
*/
public static DocAnchorEnum getByKey(String key) {
for (DocAnchorEnum docAnchorEnum : DocAnchorEnum.values()) {
if (docAnchorEnum.getKey().equalsIgnoreCase(key)) {
return docAnchorEnum;
}
}
return null;
}
}

View File

@@ -0,0 +1,70 @@
package com.njcn.gather.report.pojo.enums;
import lombok.Getter;
/**
* 检测项模版枚举
* @author hongawen
* @version 1.0
* @data 2025/3/27 10:24
*/
@Getter
public enum ItemReportKeyEnum {
NAME("name", "检测项,比如:频率"),
NAME_DETAIL("nameDetail", "检测项详细,比如:频率测量准确度"),
ERROR_SCOPE("errorScope", "误差范围,注:在段落中时需加上(),表格中无需添加"),
ERROR_SCOPE_MAG("errorScopeMag", "特征幅值:误差范围"),
ERROR_SCOPE_DUR("errorScopeDur", "持续时间:误差范围"),
SCRIPT_DETAIL("scriptDetail", "脚本输出明细。比如基波电压UN=57.74Vf=50Hz谐波含有率Uh=10%UN=5.774V"),
TIME("time", "次数"),
STANDARD("standard", "标准值"),
STANDARD_A("standardA", "A相标准值"),
STANDARD_B("standardB", "B相标准值"),
STANDARD_C("standardC", "C相标准值"),
STANDARD_MAG("standardMag", "特征幅值的标准值"),
STANDARD_DUR("standardDur_ms", "持续时间的标准值"),
TEST("test", "测试值"),
TEST_MAG("testMag", "特征幅值测试值"),
TEST_DUR("testDur_ms", "持续时间测试值"),
TEST_A("testA", "A相测试值"),
TEST_B("testB", "B相测试值"),
TEST_C("testC", "C相测试值"),
ERROR("error", "误差"),
ERROR_MAG("errorMag", "特征幅值误差"),
ERROR_DUR("errorDur_ms", "持续时间误差"),
ERROR_A("errorA", "A相误差"),
ERROR_B("errorB", "B相误差"),
ERROR_C("errorC", "C相误差"),
RESULT("result", "结论 比如:合格/不合格"),
RESULT_A("resultA", "结论 比如:合格/不合格"),
RESULT_B("resultB", "结论 比如:合格/不合格"),
RESULT_C("resultC", "结论 比如:合格/不合格"),
RESULT_MAG("resultMag", "特征幅值结论 比如:合格/不合格"),
RESULT_DUR("resultDur", "持续时间结论 比如:合格/不合格");
private String key;
private String desc;
ItemReportKeyEnum(String key, String desc) {
this.key = key;
this.desc = desc;
}
/**
* 根据key找到适配的枚举
*
* @param key 枚举的key
* @return 匹配的枚举实例如果没有找到则返回null
*/
public static ItemReportKeyEnum getByKey(String key) {
for (ItemReportKeyEnum itemReportKetEnum : ItemReportKeyEnum.values()) {
if (itemReportKetEnum.getKey().equalsIgnoreCase(key)) {
return itemReportKetEnum;
}
}
return null;
}
}

View File

@@ -0,0 +1,52 @@
package com.njcn.gather.report.pojo.enums;
import lombok.Getter;
/**
* 电能质量测试大项枚举
* @author hongawen
* @version 1.0
* @data 2025/3/27 18:29
*/
@Getter
public enum PowerIndexEnum {
UNKNOWN("UNKNOWN", "未知指标"),
FREQ("FREQ", "频率"),
V("V", "电压"),
I("I", "电流"),
IMBV("IMBV", "三相电压不平衡度"),
IMBA("IMBA", "三相电流不平衡度"),
F("F", "闪变"),
HP("HP", "谐波有功功率"),
HV("HV", "谐波电压"),
HI("HI", "谐波电流"),
HSV("HSV", "间谐波电压"),
HSI("HSI", "间谐波电流"),
VOLTAGE("VOLTAGE", "电压暂降、暂升及短时中断");
private String key;
private String desc;
PowerIndexEnum(String key, String desc) {
this.key = key;
this.desc = desc;
}
/**
* 根据key找到适配的枚举
*
* @param key 枚举的key
* @return 匹配的枚举实例如果没有找到则返回null
*/
public static PowerIndexEnum getByKey(String key) {
for (PowerIndexEnum powerIndexEnum : PowerIndexEnum.values()) {
if (powerIndexEnum.getKey().equalsIgnoreCase(key)) {
return powerIndexEnum;
}
}
return null;
}
}

View File

@@ -22,7 +22,9 @@ public enum ReportResponseEnum {
FILE_RENAME_FAILED("A012011", "文件重命名失败"),
REPORT_NAME_PATTERN_ERROR("A012012","报告名称格式错误可包含中文、字母、数字、中划线、点号、空格长度不能超过32个字符"),
REPORT_VERSION_PATTERN_ERROR("A012013","报告版本号格式错误可包含中文、字母、数字、中划线、点号、空格长度不能超过32个字符"),
FILE_SIZE_ERROR("A012014","文件大小不能超过5MB" );
FILE_SIZE_ERROR("A012014","文件大小不能超过5MB" ),
GENERATE_REPORT_ERROR("A012015","生成报告失败"),
;
private String code;
private String message;

View File

@@ -0,0 +1,40 @@
package com.njcn.gather.report.pojo.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
*
* 测试大项的检测结果
*
* @author hongawen
* @version 1.0
* @data 2025/4/7 20:17
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SingleTestResult implements Serializable {
/**
* 大项名称
*/
private String scriptCode;
/**
* 是否合格
*/
private boolean qualified;
/**
* 细节集合
*/
private Map<String, List<Map<String, List<Map<String, String>>>>> detail;
}

View File

@@ -0,0 +1,39 @@
package com.njcn.gather.report.pojo.vo;
import com.njcn.gather.report.pojo.enums.DocAnchorEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author hongawen
* @version 1.0
* @data 2025/4/7 15:36
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Bookmark implements Serializable,Comparable<Bookmark>{
/**
* 在文档中段落的索引
*/
private Integer index;
/**
* 对应枚举
*/
private DocAnchorEnum docAnchorEnum;
/**
* 根据书签的排序字段进行排序
* @param bookmark the object to be compared.
*/
@Override
public int compareTo(Bookmark bookmark) {
return Integer.compare(this.docAnchorEnum.getSort(), bookmark.docAnchorEnum.getSort());
}
}

View File

@@ -1,5 +1,6 @@
package com.njcn.gather.report.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
@@ -28,18 +29,24 @@ import com.njcn.gather.plan.service.IAdPlanService;
import com.njcn.gather.pojo.enums.DetectionResponseEnum;
import com.njcn.gather.report.mapper.PqReportMapper;
import com.njcn.gather.report.pojo.DevReportParam;
import com.njcn.gather.report.pojo.enums.ReportResponseEnum;
import com.njcn.gather.report.pojo.constant.PowerConstant;
import com.njcn.gather.report.pojo.constant.ReportConstant;
import com.njcn.gather.report.pojo.enums.*;
import com.njcn.gather.report.pojo.param.ReportParam;
import com.njcn.gather.report.pojo.po.CellEntity;
import com.njcn.gather.report.pojo.po.PqReport;
import com.njcn.gather.report.pojo.result.SingleTestResult;
import com.njcn.gather.report.pojo.vo.Bookmark;
import com.njcn.gather.report.pojo.vo.PqReportVO;
import com.njcn.gather.report.service.IPqReportService;
import com.njcn.gather.report.utils.Docx4jUtil;
import com.njcn.gather.report.utils.WordUtil;
import com.njcn.gather.result.pojo.param.ResultParam;
import com.njcn.gather.result.pojo.vo.ResultVO;
import com.njcn.gather.result.service.IResultService;
import com.njcn.gather.script.pojo.po.PqScriptCheckData;
import com.njcn.gather.script.pojo.po.PqScriptDtls;
import com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO;
import com.njcn.gather.script.service.IPqScriptCheckDataService;
import com.njcn.gather.script.service.IPqScriptDtlsService;
import com.njcn.gather.storage.pojo.param.SingleNonHarmParam;
@@ -56,22 +63,25 @@ import com.njcn.gather.system.dictionary.service.IDictTreeService;
import com.njcn.gather.system.pojo.enums.DicDataEnum;
import com.njcn.gather.type.pojo.po.DevType;
import com.njcn.gather.type.service.IDevTypeService;
import com.njcn.gather.user.user.pojo.po.SysUser;
import com.njcn.gather.user.user.service.ISysUserService;
import com.njcn.web.factory.PageFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBElement;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
@@ -229,10 +239,9 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
// 删除对应的文件
this.deleteFile(ids);
boolean result = this.lambdaUpdate().in(CollectionUtil.isNotEmpty(ids), PqReport::getId, ids)
return this.lambdaUpdate().in(CollectionUtil.isNotEmpty(ids), PqReport::getId, ids)
.set(PqReport::getState, DataStateEnum.DELETED.getCode())
.update();
return result;
}
@Override
@@ -497,62 +506,76 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
@Override
public void generateReport(DevReportParam devReportParam) {
AdPlan plan = adPlanService.getById(devReportParam.getPlanId());
if (StrUtil.isNotBlank(plan.getReportTemplateId())) {
if (plan.getAssociateReport() == 1) {
this.generateReportByPlan(plan, devReportParam);
} else {
// 根据设备类型找到报告模板
PqDevVO pqDevVO = iPqDevService.getPqDevById(devReportParam.getDevId());
if (Objects.isNull(pqDevVO)) {
throw new BusinessException("请检查装置是否存在!");
}
// 获取设备型号
DevType devType = devTypeService.getById(pqDevVO.getDevType());
if (Objects.isNull(devType)) {
throw new BusinessException("设备类型缺失,请联系管理员!");
}
DictData reportName = devTypeService.getReportName(pqDevVO.getDevType());
if (Objects.isNull(reportName)) {
throw new BusinessException("报告模板缺失,请联系管理员!");
}
// 读取模板文件
ClassPathResource resource = new ClassPathResource("/model/" + reportName.getCode() + ".docx");
try (InputStream inputStream = resource.getInputStream()) {
// 加载Word文档
XWPFDocument baseModelDocument = new XWPFDocument(inputStream);
// 处理基础模版中的信息
dealBaseModel(baseModelDocument, pqDevVO, devType);
// 处理数据页中的信息
dealDataModel(baseModelDocument, devReportParam, pqDevVO);
// 处理需要输出的目录地址 基础路径+设备类型+装置编号.docx
// 最终文件输出的路径
String dirPath = reportPath.concat(File.separator).concat(devType.getName());
ensureDirectoryExists(dirPath); // 确保目录存在
FileOutputStream out = new FileOutputStream(dirPath.concat(File.separator).concat(pqDevVO.getCreateId()).concat(".docx"));
// 4. 保存新的Word文档
try {
baseModelDocument.write(out);
} catch (IOException e) {
throw new BusinessException("生成报告文件失败");
}
out.close();
} else if (plan.getAssociateReport() == 0) {
this.generateReportByDevType(plan, devReportParam);
}
}
System.out.println("报告生成成功!");
this.updateDevAndPlanState(devReportParam.getDevId(), devReportParam.getPlanId());
/**
* 根据设备类型生成报告
* 注:该方法目前仅支持楼下出厂检测场景,属于模板占位符替换方式,后期可能会有调整
*
* @param plan 计划信息
* @param devReportParam 被检设备信息
*/
private void generateReportByDevType(AdPlan plan, DevReportParam devReportParam) {
// 根据设备类型找到报告模板
PqDevVO pqDevVO = iPqDevService.getPqDevById(devReportParam.getDevId());
if (Objects.isNull(pqDevVO)) {
throw new BusinessException(ReportResponseEnum.DEVICE_NOT_EXIST);
}
// 获取设备型号
DevType devType = devTypeService.getById(pqDevVO.getDevType());
if (Objects.isNull(devType)) {
throw new BusinessException(ReportResponseEnum.DEVICE_TYPE_NOT_EXIST);
}
DictData reportName = devTypeService.getReportName(pqDevVO.getDevType());
if (Objects.isNull(reportName)) {
throw new BusinessException(ReportResponseEnum.REPORT_TEMPLATE_NOT_EXIST);
}
// 读取模板文件
ClassPathResource resource = new ClassPathResource("/model/" + reportName.getCode() + ReportConstant.DOCX);
try (InputStream inputStream = resource.getInputStream()) {
// 加载Word文档
XWPFDocument baseModelDocument = new XWPFDocument(inputStream);
// 处理基础模版中的信息
Map<String, String> baseModelDataMap = dealBaseModelData(pqDevVO, devType, "${", "}");
// 替换模板中的信息,避免信息丢失,段落和表格均参与替换
WordUtil.replacePlaceholders(baseModelDocument, baseModelDataMap);
// 处理数据页中的信息
dealDataModel(baseModelDocument, devReportParam, pqDevVO);
// 处理需要输出的目录地址 基础路径+设备类型+装置编号.docx
// 最终文件输出的路径
String dirPath = reportPath.concat(File.separator).concat(devType.getName());
// 确保目录存在
ensureDirectoryExists(dirPath);
FileOutputStream out = new FileOutputStream(dirPath.concat(File.separator).concat(pqDevVO.getCreateId()).concat(".docx"));
// 4. 保存新的Word文档
try {
baseModelDocument.write(out);
} catch (IOException e) {
log.error("生成报告文件失败", e);
throw new RuntimeException(e);
throw new BusinessException(ReportResponseEnum.GENERATE_REPORT_ERROR);
}
out.close();
this.updateDevAndPlanState(devReportParam.getDevId(), devReportParam.getPlanId());
} catch (IOException e) {
log.error(ReportResponseEnum.GENERATE_REPORT_ERROR.getMessage(), e);
throw new BusinessException(ReportResponseEnum.GENERATE_REPORT_ERROR);
}
}
/**
* 根据计划绑定的报告模板生成报告
* 注:该方法目前属于同用信息占位符替换,数据页为面向对象动态填充拼凑方式
*
* @param plan
* @param devReportParam
* @param plan 计划信息
* @param devReportParam 设备信息
*/
private void generateReportByPlan(AdPlan plan, DevReportParam devReportParam) {
// 根据设备类型找到报告模板
// 准备被检设备的基础信息
PqDevVO pqDevVO = iPqDevService.getPqDevById(devReportParam.getDevId());
if (Objects.isNull(pqDevVO)) {
throw new BusinessException(ReportResponseEnum.DEVICE_NOT_EXIST);
@@ -566,41 +589,397 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
if (Objects.isNull(report)) {
throw new BusinessException(ReportResponseEnum.REPORT_TEMPLATE_NOT_EXIST);
}
FileSystemResource resource = new FileSystemResource(report.getBasePath());
try (InputStream inputStream = resource.getInputStream()) {
// 加载Word文档
XWPFDocument baseModelDocument = new XWPFDocument(inputStream);
// 处理基础模版中的信息
dealBaseModel(baseModelDocument, pqDevVO, devType);
// 处理数据页中的信息
dealDataModelZJ(baseModelDocument, devReportParam, pqDevVO);
// 处理需要输出的目录地址 基础路径+设备类型+装置编号.docx
// 最终文件输出的路径
try {
WordprocessingMLPackage baseModelDocument = WordprocessingMLPackage.load(new File(report.getBasePath()));
WordprocessingMLPackage detailModelDocument = WordprocessingMLPackage.load(new File(report.getDetailPath()));
// 获取文档基础部分,并替换占位符
MainDocumentPart baseDocumentPart = baseModelDocument.getMainDocumentPart();
Map<String, String> baseModelDataMap = dealBaseModelData(pqDevVO, devType, "", "");
baseDocumentPart.variableReplace(baseModelDataMap);
// 获取数据模版页内容,根据脚本动态组装数据页内容
MainDocumentPart detailDocumentPart = detailModelDocument.getMainDocumentPart();
dealDataModelScattered(baseDocumentPart, detailDocumentPart, devReportParam, pqDevVO);
// 保存新的文档
String dirPath = reportPath.concat(File.separator).concat(devType.getName());
ensureDirectoryExists(dirPath); // 确保目录存在
FileOutputStream out = new FileOutputStream(dirPath.concat(File.separator).concat(pqDevVO.getCreateId()).concat(".docx"));
// 4. 保存新的Word文档
try {
baseModelDocument.write(out);
} catch (IOException e) {
throw new BusinessException("生成报告文件失败");
}
out.close();
System.out.println("报告生成成功!");
this.updateDevAndPlanState(devReportParam.getDevId(), devReportParam.getPlanId());
} catch (IOException e) {
log.error("生成报告文件失败", e);
throw new RuntimeException(e);
// 确保目录存在
ensureDirectoryExists(dirPath);
baseModelDocument.save(new File(dirPath.concat(File.separator).concat(pqDevVO.getCreateId()).concat(ReportConstant.DOCX)));
} catch (Exception e) {
log.error(ReportResponseEnum.GENERATE_REPORT_ERROR.getMessage(), e);
throw new BusinessException(ReportResponseEnum.GENERATE_REPORT_ERROR);
}
}
/**
* 通用基础信息文档里可能会存在的书签锚点
* 1、目录信息
* 2、准确度测试详情
* 3、测试结果页
* 上述3个锚点位置不固定可能结果页在通用信息中间也有可能在文档最末端。
* 注:当存在目录信息时,目录最后生成。
*
* @param baseDocumentPart 通用信息文档
* @param detailDocumentPart 数据项模板文档
* @param devReportParam 测试报告参数
* @param pqDevVO 被检设备
*/
private void dealDataModelScattered(MainDocumentPart baseDocumentPart, MainDocumentPart detailDocumentPart, DevReportParam devReportParam, PqDevVO pqDevVO) {
// 查找 base 文档中所有以#{开始的书签所在的段落,并收集整理
List<Object> baseContent = baseDocumentPart.getContent();
List<Bookmark> bookmarks = new ArrayList<>();
for (int i = 0; i < baseContent.size(); i++) {
Object obj = baseContent.get(i);
if (obj instanceof P) {
P p = (P) obj;
String text = Docx4jUtil.getTextFromP(p).trim();
if (text.startsWith(ReportConstant.BOOKMARK_START)) {
Bookmark bookmark = new Bookmark(i, DocAnchorEnum.getByKey(text));
bookmarks.add(bookmark);
}
}
}
if (CollUtil.isNotEmpty(bookmarks)) {
/*
* 从结构上分析,处理的顺序:
* 1、数据项
* 2、结果信息
* 3、目录信息
* 所以要先先获取的书签进行操作排序
* */
Collections.sort(bookmarks);
// 定义个结果,以便存在结果信息的书签
Map<String/*指标名称*/, List<Boolean/*以回路的顺序填充结果*/>> resultMap = new HashMap<>();
// 书签在文档的位置
int position;
for (int i = 0; i < bookmarks.size(); i++) {
Bookmark bookmark = bookmarks.get(i);
if (i == 0) {
position = bookmark.getIndex();
} else {
// 经过处理后,原本的书签位置,已经被处理,所以需要重新获取
position = Docx4jUtil.getParagraphPosition(baseDocumentPart, bookmark.getDocAnchorEnum());
}
switch (bookmark.getDocAnchorEnum()) {
case DATA_LINE:
dealDataLine(baseDocumentPart, detailDocumentPart, devReportParam, pqDevVO, position, resultMap);
break;
case DATA_SCRIPT:
break;
case TEST_RESULT_DEV:
// 判断是否已经处理过数据了,有了结论性的描述
if (CollUtil.isEmpty(resultMap)) {
dealDataLine(baseDocumentPart, detailDocumentPart, devReportParam, pqDevVO, position, resultMap);
}
dealTestResultLine(baseDocumentPart, detailDocumentPart, position, resultMap, DocAnchorEnum.TEST_RESULT_DEV);
break;
case TEST_RESULT_LINE:
// 判断是否已经处理过数据了,有了结论性的描述
if (CollUtil.isEmpty(resultMap)) {
dealDataLine(baseDocumentPart, detailDocumentPart, devReportParam, pqDevVO, position, resultMap);
}
dealTestResultLine(baseDocumentPart, detailDocumentPart, position, resultMap, DocAnchorEnum.TEST_RESULT_LINE);
break;
case TEST_RESULT_DETAIL:
// 判断是否已经处理过数据了,有了结论性的描述
if (CollUtil.isEmpty(resultMap)) {
dealDataLine(baseDocumentPart, detailDocumentPart, devReportParam, pqDevVO, position, resultMap);
}
dealTestResultLine(baseDocumentPart, detailDocumentPart, position, resultMap, DocAnchorEnum.TEST_RESULT_DETAIL);
break;
case CATALOG:
break;
default:
break;
}
}
}
}
/**
* 如何处理结果性数据进文档,各省级平台的结果表格不一致,如何做到一致
*
* @param baseDocumentPart 基础模板文档
* @param detailDocumentPart 数据模板文档
* @param position 书签在基础文档的位置
* @param resultMap 各测试项的结果
*/
private void dealTestResultLine(MainDocumentPart baseDocumentPart, MainDocumentPart detailDocumentPart, int position, Map<String, List<Boolean>> resultMap, DocAnchorEnum docAnchorEnum) {
// 先判断数据有没有,如果没有,则不处理
if (CollUtil.isEmpty(resultMap.get(PowerIndexEnum.UNKNOWN.getKey()))) {
List<Object> paragraphs = baseDocumentPart.getContent();
ObjectFactory factory = Context.getWmlObjectFactory();
// 结论
P newParagraph = factory.createP();
Docx4jUtil.createTitle(factory, newParagraph, "检测结论", 28, true);
//插入段落
paragraphs.add(position++, newParagraph);
// 源文档的内容
// 创建表格示例为3列列数可任意调整
Tbl table = factory.createTbl();
List<List<Boolean>> tempResultList = new ArrayList<>(resultMap.values());
int lineNum = tempResultList.get(0).size();
// 处理表头
List<String> title = new ArrayList<>();
title.add("检测项目");
switch (docAnchorEnum) {
case TEST_RESULT_DEV:
title.add("结论");
break;
case TEST_RESULT_LINE:
for (int i = 1; i < lineNum + 1; i++) {
title.add("测量回路" + i);
}
break;
case TEST_RESULT_DETAIL:
for (int i = 1; i < lineNum + 1; i++) {
title.add("测量回路" + i);
}
title.add("结论");
break;
default:
break;
}
Tr titleRow = Docx4jUtil.createCustomRow(factory, title, "SimSun", "宋体", 21, true, false);
table.getContent().add(titleRow);
// 处理业务数据
resultMap.forEach((key, value) -> {
List<String> cellValues = new ArrayList<>();
PowerIndexEnum indexEnum = PowerIndexEnum.getByKey(key);
if (indexEnum != null) {
cellValues.add(indexEnum.getDesc().concat("测量准确度"));
} else {
cellValues.add(PowerIndexEnum.UNKNOWN.getDesc().concat("测量准确度"));
}
// 判断是否有不合格的
List<Boolean> totalValue = value.stream().filter(item -> !item).collect(Collectors.toList());
String total = !totalValue.isEmpty() ? "不合格" : "合格";
switch (docAnchorEnum) {
case TEST_RESULT_DEV:
cellValues.add(total);
break;
case TEST_RESULT_LINE:
for (int i = 0; i < value.size(); i++) {
cellValues.add(value.get(i) ? "合格" : "不合格");
}
break;
case TEST_RESULT_DETAIL:
for (int i = 0; i < value.size(); i++) {
cellValues.add(value.get(i) ? "合格" : "不合格");
}
cellValues.add(total);
break;
default:
break;
}
Tr tempRow = Docx4jUtil.createCustomRow(factory, cellValues, "SimSun", "宋体", 21, false, false);
table.getContent().add(tempRow);
});
TblPr tblPr = Docx4jUtil.getTblPr(factory);
table.setTblPr(tblPr);
paragraphs.add(position++, table);
// 标签的位置,以便清空原标签
if (position >= 0 && position < paragraphs.size()) {
paragraphs.remove(position);
} else {
System.out.println("指定的索引超出范围");
}
}
}
/**
* 处理以回路为维度的数据项
*
* @param baseModelDocument 基础模板
* @param detailModelDocument 数据项模板
* @param devReportParam 测试报告参数
* @param pqDevVO 被检设备
* @param position 待处理书签的位置
*/
private void dealDataLine(MainDocumentPart baseModelDocument, MainDocumentPart detailModelDocument, DevReportParam devReportParam, PqDevVO pqDevVO, int position, Map<String, List<Boolean>> resultMap) {
// 源文档的内容
List<Object> paragraphs = baseModelDocument.getContent();
// 以回路维度处理数据项
Integer devChns = pqDevVO.getDevChns();
ObjectFactory factory = new ObjectFactory();
// 读取该计划的检测大项组装数据内容
List<PqScriptDtlDataVO> pqScriptDtlsList = pqScriptDtlsService.getScriptDtlsDataList(devReportParam.getScriptId());
Map<String, List<PqScriptDtlDataVO>> scriptMap = pqScriptDtlsList.stream().collect(Collectors.groupingBy(PqScriptDtlDataVO::getScriptCode));
List<Object> allContent = detailModelDocument.getContent();
List<Docx4jUtil.HeadingContent> headingContents = Docx4jUtil.extractHeading5Contents(allContent);
Map<String, List<Docx4jUtil.HeadingContent>> contentMap = headingContents.stream().collect(Collectors.groupingBy(Docx4jUtil.HeadingContent::getHeadingText, Collectors.toList()));
for (int i = 0; i < devChns; i++) {
// 回路标题
P newParagraph = factory.createP();
Integer lineNo = i + 1;
Docx4jUtil.createTitle(factory, newParagraph, "测量回路" + lineNo, 28, true);
//插入段落
paragraphs.add(position++, newParagraph);
// 依次处理大项文档内容
Iterator<Map.Entry<String, List<PqScriptDtlDataVO>>> iterator = scriptMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, List<PqScriptDtlDataVO>> next = iterator.next();
String scriptCode = next.getKey();
List<PqScriptDtlDataVO> checkckItemList = next.getValue();
List<Docx4jUtil.HeadingContent> tempContent = contentMap.get(scriptCode);
// 获取需要填充keys索引0对应的段落key索引1对应的表格key
List<List<String>> keys = Docx4jUtil.getFillKeys(tempContent);
// 段落keys值赋值
List<String> pKeys = keys.get(0);
Map<String, String> pKeyValueMap = resultService.getParagraphKeysValue(scriptCode, pKeys);
List<String> tableKeys = keys.get(1);
/* tableKeys值赋值由于谐波类检测数据与非谐波检测类数据的区别此处要做区分
* 1、谐波类每个scriptIndex对应一个Excel表格
* 2、非谐波类则是一个误差范围对应一个Excel表格
*/
SingleTestResult singleTestResult = null;
// 根据code找到名称
List<Boolean> scriptResult = resultMap.get(scriptCode);
boolean needFill = false;
if (CollUtil.isEmpty(scriptResult)) {
scriptResult = new ArrayList<>();
needFill = true;
} else if (scriptResult.size() < lineNo) {
needFill = true;
}
if (PowerConstant.TIME.contains(scriptCode)) {
// 谐波类以scriptIndex区分
Map<Integer, List<PqScriptDtlDataVO>> scriptIndexMap = checkckItemList.stream().collect(Collectors.groupingBy(PqScriptDtlDataVO::getScriptIndex));
for (List<PqScriptDtlDataVO> scriptDtlDataItem : scriptIndexMap.values()) {
singleTestResult = resultService.getFinalContent(scriptDtlDataItem, devReportParam.getPlanCode(), pqDevVO.getId(), lineNo, tableKeys);
position = fillContentInTemplate(singleTestResult.getDetail(), position, tempContent, factory, pKeyValueMap, tableKeys, paragraphs);
}
} else {
// 非谐波类
singleTestResult = resultService.getFinalContent(checkckItemList, devReportParam.getPlanCode(), pqDevVO.getId(), lineNo, tableKeys);
position = fillContentInTemplate(singleTestResult.getDetail(), position, tempContent, factory, pKeyValueMap, tableKeys, paragraphs);
}
if (Objects.nonNull(singleTestResult) && needFill) {
singleTestResult.setScriptCode(scriptCode);
scriptResult.add(singleTestResult.isQualified());
resultMap.put(scriptCode, scriptResult);
}
}
}
// 如果经过一顿处理后,结果性数据集合还是空,塞个特殊数据进去,避免嵌套循环
if (CollUtil.isEmpty(resultMap)) {
resultMap.put(PowerIndexEnum.UNKNOWN.getKey(), Collections.singletonList(false));
}
// 标签的位置,以便清空原标签
if (position >= 0 && position < paragraphs.size()) {
paragraphs.remove(position);
} else {
System.out.println("指定的索引超出范围");
}
}
/**
* 将查询的所有有效数据填充到模板中
*/
private int fillContentInTemplate(Map<String, List<Map<String, List<Map<String, String>>>>> finalContent, int position, List<Docx4jUtil.HeadingContent> tempContent,
ObjectFactory factory, Map<String, String> pKeyValueMap, List<String> tableKeys, List<Object> paragraphs) {
if (CollUtil.isNotEmpty(finalContent)) {
Iterator<Map.Entry<String, List<Map<String, List<Map<String, String>>>>>> iterator1 = finalContent.entrySet().iterator();
while (iterator1.hasNext()) {
Map.Entry<String, List<Map<String, List<Map<String, String>>>>> next1 = iterator1.next();
// 此处的key是影响量的文字描述
String key = next1.getKey();
List<Map<String, List<Map<String, String>>>> value = next1.getValue();
for (Map<String, List<Map<String, String>>> stringListMap : value) {
Iterator<Map.Entry<String, List<Map<String, String>>>> iterator2 = stringListMap.entrySet().iterator();
while (iterator2.hasNext()) {
Map.Entry<String, List<Map<String, String>>> next2 = iterator2.next();
// 此处的key是误差范围
String key1 = next2.getKey();
List<Map<String, String>> value1 = next2.getValue();
// 填充模板内容
if (CollUtil.isNotEmpty(tempContent)) {
// 读取该表下模板里面的内容,并动态赋值渲染文档
Docx4jUtil.HeadingContent headingContent = tempContent.get(0);
for (Object object : headingContent.getSubContent()) {
// 段落
if (object instanceof P) {
P innerP = factory.createP();
// 如果是段落,渲染段落内容
P paragraph = (P) object;
// 获取该段落的样式
RPr rPr = Docx4jUtil.getTcPrFromParagraph(paragraph);
String textFromP = Docx4jUtil.getTextFromP(paragraph);
if (StrUtil.isNotBlank(textFromP)) {
// 如果是段落内容,渲染段落内容
String[] splitP = textFromP.split(StrPool.DASHED);
String text = "";
for (String item : splitP) {
if (StrUtil.isNotBlank(pKeyValueMap.get(item))) {
text = text.concat(pKeyValueMap.get(item));
} else if (item.equalsIgnoreCase(ItemReportKeyEnum.ERROR_SCOPE.getKey())) {
text = text.concat("(").concat("最大允许误差:").concat(key1).concat(")");
}
// 目前段落只有名称+误差范围如果有补充后续在这里加。todo...
}
Docx4jUtil.addPContent(factory, innerP, text, rPr);
}
//插入段落
paragraphs.add(position++, innerP);
} else if (object instanceof JAXBElement) {
// 表格需要注意深拷贝,避免修改了原对象
JAXBElement<Tbl> temp = (JAXBElement<Tbl>) object;
JAXBElement<Tbl> copiedTableElement;
try {
copiedTableElement = Docx4jUtil.deepCopyTbl(temp);
} catch (Exception e) {
throw new RuntimeException(e);
}
// 解析表格并插入对应数据,最关键的是得知道表格是横向还是纵向以及表头占了几行
Tbl tbl = copiedTableElement.getValue();
// 获取表格的行
List<Object> rows = tbl.getContent();
boolean isRow = Docx4jUtil.judgeTableCross(rows.get(0));
if (isRow) {
// 删除最后一行这行用来填模板key的无需展示
// 获取现有行的样式
Tr existingRow = (Tr) tbl.getContent().get(rows.size() - 1);
// 获取现有样式
TrPr trPr = existingRow.getTrPr();
JAXBElement<Tc> element = (JAXBElement<Tc>) existingRow.getContent().get(0);
TcPr tcPr = element.getValue().getTcPr();
tbl.getContent().remove(existingRow);
// 迭代增加行,需要填充的表格keys在tableKeys集合中
for (Map<String, String> stringStringMap : value1) {
Tr newRow = Docx4jUtil.createCustomRow(factory, stringStringMap, tableKeys, trPr, tcPr, true);
tbl.getContent().add(newRow);
}
} else {
// 纵向表格暂不考虑
}
// 插入段落
paragraphs.add(position++, copiedTableElement);
}
}
// 全部渲染完毕后,添加几个换行
P p = factory.createP();
Docx4jUtil.addBr(factory, p, 2);
paragraphs.add(position++, p);
}
}
}
}
}
return position;
}
private void updateDevAndPlanState(String devId, String planId) {
// 将改设备的报告生成状态调整为已生成
iPqDevService.updatePqDevReportState(devId, DevReportStateEnum.GENERATED.getValue());
// 判断该计划下是否所有设备报告已生成,如果已生成则将计划的报告状态给为已生成
int count = iPqDevService.countUnReportDev(planId);
LambdaUpdateWrapper<AdPlan> updateWrapper = new LambdaUpdateWrapper<>();
@@ -656,62 +1035,55 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
}
/**
* 处理基础模版中的信息,非数据页报告
*
* @param baseModelDocument 模板文件
* 此处为什么要抽出拼接的前缀&后缀是因为Docx4j工具包替换时会默认增加${}故在使用docx4j时前后缀必须为空
*/
private void dealBaseModel(XWPFDocument baseModelDocument, PqDevVO pqDevVO, DevType devType) {
private Map<String, String> dealBaseModelData(PqDevVO pqDevVO, DevType devType, String prefix, String suffix) {
// 首先获取非数据页中需要的信息
Map<String, String> baseModelMap = new HashMap<>(16);
Map<String, String> baseModelMap = new HashMap<>(32);
// 获取设备型号
baseModelMap.put("${devType}", devType.getName());
baseModelMap.put("${device_type}", devType.getName());
// 调试人员todo... 待咨询曹泽辉如何获取当前用户信息,目前先写死
//String userName = RequestUtil.getUserName();
SysUser user = sysUserService.getById(pqDevVO.getCheckBy());
baseModelMap.put("${userName}", user.getName());
baseModelMap.put(prefix + BaseReportKeyEnum.DEV_TYPE.getKey() + suffix, devType.getName());
// 检测员
baseModelMap.put(prefix + BaseReportKeyEnum.INSPECTOR.getKey() + suffix, pqDevVO.getCheckBy());
// 调试日期
if (pqDevVO.getCheckTime() != null) {
baseModelMap.put("${testDate}", DateUtil.format(pqDevVO.getCheckTime(), DatePattern.CHINESE_DATE_PATTERN));
baseModelMap.put(prefix + BaseReportKeyEnum.TEST_DATE.getKey() + suffix, DateUtil.format(pqDevVO.getCheckTime(), DatePattern.CHINESE_DATE_PATTERN));
} else {
baseModelMap.put("${testDate}", DateUtil.format(new Date(), DatePattern.CHINESE_DATE_PATTERN));
baseModelMap.put(prefix + BaseReportKeyEnum.TEST_DATE.getKey() + suffix, DateUtil.format(new Date(), DatePattern.CHINESE_DATE_PATTERN));
}
// 装置编码
baseModelMap.put("${CreateId}", pqDevVO.getCreateId());
baseModelMap.put(prefix + BaseReportKeyEnum.DEV_CODE.getKey() + suffix, pqDevVO.getCreateId());
// 工作电源
baseModelMap.put("${power}", devType.getPower());
baseModelMap.put(prefix + BaseReportKeyEnum.POWER.getKey() + suffix, devType.getPower());
// 额定电流
baseModelMap.put("${devCurr}", pqDevVO.getDevCurr().toString().concat("A"));
baseModelMap.put(prefix + BaseReportKeyEnum.DEV_CURR.getKey() + suffix, pqDevVO.getDevCurr().toString().concat(PowerConstant.CURRENT_UNIT));
// 额定电压
baseModelMap.put("${devVolt}", pqDevVO.getDevVolt().toString().concat("V"));
// 共有多少通道参与测试
// if (CollectionUtil.isEmpty(pqDevById.getMonitorList())) {
// baseModelMap.put("${count}", "0");
// } else {
// baseModelMap.put("${count}", pqDevById.getMonitorList().size() + "");
// }
baseModelMap.put("${count}", pqDevVO.getDevChns().toString());
baseModelMap.put(prefix + BaseReportKeyEnum.DEV_VOLT.getKey() + suffix, pqDevVO.getDevVolt().toString().concat(PowerConstant.VOLTAGE_UNIT));
// 通道数
baseModelMap.put(prefix + BaseReportKeyEnum.COUNT.getKey() + suffix, pqDevVO.getDevChns().toString());
// 制造厂家
DictData dictData = dictDataService.getDictDataById(pqDevVO.getManufacturer());
if (ObjectUtil.isNotNull(dictData)) {
baseModelMap.put("${manufacturer}", dictData.getName());
baseModelMap.put(prefix + BaseReportKeyEnum.MANUFACTURER.getKey() + suffix, dictData.getName());
} else {
baseModelMap.put("${manufacturer}", "未知");
baseModelMap.put(prefix + BaseReportKeyEnum.MANUFACTURER.getKey() + suffix, StrPool.TAB);
}
baseModelMap.put("${sample_id}", String.valueOf(pqDevVO.getSampleId()));
baseModelMap.put("${arrived_date}", String.valueOf(pqDevVO.getArrivedDate()));
baseModelMap.put("${check_date}", String.valueOf(pqDevVO.getCheckTime()).substring(0, 10));
baseModelMap.put("${tested_by}", user.getName());
// 替换模板中的信息,避免信息丢失,段落和表格均参与替换
WordUtil.replacePlaceholdersInParagraphs(baseModelDocument, baseModelMap);
WordUtil.replacePlaceholdersInTables(baseModelDocument, baseModelMap);
// 样品编号
baseModelMap.put(prefix + BaseReportKeyEnum.SAMPLE_ID.getKey() + suffix, StrUtil.isEmpty(pqDevVO.getSampleId()) ? StrPool.TAB : pqDevVO.getSampleId());
// 收样日期
baseModelMap.put(prefix + BaseReportKeyEnum.ARRIVED_DATE.getKey() + suffix, Objects.isNull(pqDevVO.getArrivedDate()) ? StrPool.TAB : String.valueOf(pqDevVO.getArrivedDate()));
// 检测日期
baseModelMap.put(prefix + BaseReportKeyEnum.TEST_DATE.getKey() + suffix, Objects.isNull(pqDevVO.getCheckTime()) ? StrPool.TAB : String.valueOf(pqDevVO.getCheckTime()).substring(0, 10));
baseModelMap.put(prefix + BaseReportKeyEnum.YEAR.getKey() + suffix, DateUtil.format(new Date(), DatePattern.NORM_YEAR_PATTERN));
baseModelMap.put(prefix + BaseReportKeyEnum.MONTH.getKey() + suffix, DateUtil.format(new Date(), DatePattern.SIMPLE_MONTH_PATTERN).substring(4));
baseModelMap.put(prefix + BaseReportKeyEnum.DAY.getKey() + suffix, DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN).substring(6));
baseModelMap.put(prefix + BaseReportKeyEnum.YEAR_MONTH_DAY.getKey() + suffix, DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN));
return baseModelMap;
}
/**
* 获取数据页的信息
*

View File

@@ -0,0 +1,44 @@
package com.njcn.gather.report.utils;
/**
* @author hongawen
* @version 1.0
* @data 2025/3/25 19:37
*/
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import java.io.File;
import java.math.BigInteger;
import java.util.List;
public class Docx4jInsertParagraph {
public static void main(String[] args) throws Exception {
// 加载现有的 Word 文档
WordprocessingMLPackage wordPackage = WordprocessingMLPackage.load(new File("C:\\Users\\hongawen\\Desktop\\base_template.docx"));
MainDocumentPart documentPart = wordPackage.getMainDocumentPart();
// 获取文档中的所有段落
List<Object> paragraphs = documentPart.getContent();
// 在中间插入一个新段落
int insertIndex = paragraphs.size() / 2;
P newParagraph = createParagraph("This is a new paragraph inserted in the middle.");
paragraphs.add(insertIndex, newParagraph);
// 保存修改后的文档
wordPackage.save(new File("example_modified.docx"));
}
private static P createParagraph(String text) {
ObjectFactory factory = new ObjectFactory();
P paragraph = factory.createP();
R run = factory.createR();
Text t = factory.createText();
t.setValue(text);
run.getContent().add(t);
paragraph.getContent().add(run);
return paragraph;
}
}

View File

@@ -0,0 +1,537 @@
package com.njcn.gather.report.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import com.njcn.gather.report.pojo.constant.ReportConstant;
import com.njcn.gather.report.pojo.enums.DocAnchorEnum;
import com.njcn.gather.report.pojo.vo.Bookmark;
import org.docx4j.XmlUtils;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author hongawen
* @version 1.0
* @data 2025/3/26 13:47
*/
public class Docx4jUtil {
/**
* 创建标题
*
* @param factory 工厂
* @param paragraph 段落容器
* @param content 标题内容
* @param fontSize 字体大小
* @param isBold 是否加粗
*/
public static void createTitle(ObjectFactory factory, P paragraph, String content, int fontSize, boolean isBold) {
R run = factory.createR();
Text text = factory.createText();
text.setValue(content);
// 创建运行属性
RPr rPr = factory.createRPr();
// 设置字体
RFonts fonts = factory.createRFonts();
fonts.setAscii("Arial");
// 宋体
fonts.setEastAsia("SimSun");
rPr.setRFonts(fonts);
// 设置字号
HpsMeasure size = new HpsMeasure();
// 12号字=24
size.setVal(new BigInteger("" + fontSize));
rPr.setSz(size);
rPr.setSzCs(size);
// 设置粗体
if (isBold) {
BooleanDefaultTrue b = new BooleanDefaultTrue();
rPr.setB(b);
}
run.setRPr(rPr);
run.getContent().add(text);
// 换行
// Br br = factory.createBr();
// run.getContent().add(br);
paragraph.getContent().add(run);
}
/**
* 提取Heading 5及其子内容
*
* @param allContent 文档内所有内容
*/
public static List<HeadingContent> extractHeading5Contents(List<Object> allContent) {
List<HeadingContent> result = new ArrayList<>();
boolean inHeading5Section = false;
HeadingContent currentHeading = null;
for (Object obj : allContent) {
if (obj instanceof P) {
P paragraph = (P) obj;
if (isHeading5(paragraph)) {
// 发现新的Heading 5保存前一个并创建新的
if (currentHeading != null) {
result.add(currentHeading);
}
currentHeading = new HeadingContent();
currentHeading.setHeadingText(getTextFromP(paragraph));
inHeading5Section = true;
} else if (inHeading5Section) {
// 当前内容属于Heading 5的子内容
currentHeading.addSubContent(paragraph);
}
} else if (obj instanceof JAXBElement && inHeading5Section) {
// 表格属于当前Heading 5的子内容
JAXBElement<?> jaxbElement = (JAXBElement<?>) obj;
if (jaxbElement.getValue() instanceof Tbl) {
currentHeading.addSubContent(obj);
}
} else if (isHigherLevelHeading(obj)) {
// 遇到更高级别的标题结束当前Heading 5的收集
if (currentHeading != null) {
result.add(currentHeading);
currentHeading = null;
}
inHeading5Section = false;
}
}
// 添加最后一个Heading 5
if (currentHeading != null) {
result.add(currentHeading);
}
return result;
}
// 判断段落是否为Heading 5
private static boolean isHeading5(P paragraph) {
PPr ppr = paragraph.getPPr();
if (ppr != null) {
PPrBase.PStyle pStyle = ppr.getPStyle();
if (pStyle != null && "5".equals(pStyle.getVal())) {
return true;
}
}
return false;
}
// 判断是否为更高级别的标题1-4
private static boolean isHigherLevelHeading(Object obj) {
if (obj instanceof P) {
PPr ppr = ((P) obj).getPPr();
if (ppr != null) {
PPrBase.PStyle pStyle = ppr.getPStyle();
if (pStyle != null) {
String style = pStyle.getVal();
return style != null && style.matches("[1-4]");
}
}
}
return false;
}
/**
* 判断表格是否横向
*
* @param obj row
*/
public static boolean judgeTableCross(Object obj) {
Tr row = (Tr) obj;
List<Object> content = row.getContent();
// 取最后一个单元格,判断是否包含汉字,有汉字就是横向的
Object cellObject = content.get(content.size() - 1);
JAXBElement<Tc> cellElement = (JAXBElement<Tc>) cellObject;
Tc cell = cellElement.getValue();
String text = getTextFromCell(cell);
if (StrUtil.isBlank(text)) {
return true;
}
// 遍历字符串中的每个字符存在中文就认为是横向的理论上设置的key全是英文
for (char c : text.toCharArray()) {
// 检查字符是否在中文Unicode范围内
if (c >= '\u4E00' && c <= '\u9FFF') {
// 发现中文字符,返回 false
return true;
}
}
return false;
}
/**
* 读取cell内的文本内容
*/
public static String getTextFromCell(Tc cell) {
List<Object> cellContent = cell.getContent();
StringBuilder cellText = new StringBuilder();
for (Object content : cellContent) {
if (content instanceof P) {
P paragraph = (P) content;
cellText.append(getTextFromP(paragraph));
}
}
return cellText.toString();
}
/**
* 从段落中提取纯文本
*
* @param paragraph 段落
* @return 段落内容
*/
public static String getTextFromP(P paragraph) {
StringBuilder textContent = new StringBuilder();
for (Object runObj : paragraph.getContent()) {
if (runObj instanceof R) {
R run = (R) runObj;
for (Object textObj : run.getContent()) {
if (textObj instanceof Text) {
textContent.append(((Text) textObj).getValue());
} else if (textObj instanceof JAXBElement) {
JAXBElement<?> jaxbElement = (JAXBElement<?>) textObj;
if (jaxbElement.getValue() instanceof Text) {
Text temp = (Text) jaxbElement.getValue();
textContent.append(temp.getValue());
}
}
}
}
}
return textContent.toString().trim();
}
/**
* 获取段落的样式
*
* @param paragraph 段落
*/
public static RPr getTcPrFromParagraph(P paragraph) {
// 1. 获取段落中的所有内容
List<Object> content = paragraph.getContent();
// 2. 清空原有内容但保留第一个Run的样式
RPr preservedRPr = null;
if (!content.isEmpty()) {
Object firstObj = content.get(0);
if (firstObj instanceof R) {
// 保存第一个Run的样式
preservedRPr = ((R) firstObj).getRPr();
}
}
return preservedRPr;
}
/**
* 段落中添加内容
*/
public static void addPContent(ObjectFactory factory, P paragraph, String content, RPr rPr) {
R run = factory.createR();
Text text = factory.createText();
text.setValue(content);
run.setRPr(rPr);
run.getContent().add(text);
paragraph.getContent().add(run);
}
/**
* 创建N个换行符
*/
public static void addBr(ObjectFactory factory, P paragraph, int n) {
R run = factory.createR();
for (int i = 0; i < n; i++) {
// 换行
Br br = factory.createBr();
run.getContent().add(br);
}
paragraph.getContent().add(run);
}
/**
* 根据表格行获取需要填的值的key
*/
public static List<String> getTableKey(Tr row) {
List<String> keys = new ArrayList<>();
// 横向的,最后一行为需要填充的数据后,前面的均是表头
// 遍历获取出该row的所有key
List<Object> content = row.getContent();
for (Object cellObject : content) {
if (cellObject instanceof JAXBElement) {
JAXBElement<Tc> cellElement = (JAXBElement<Tc>) cellObject;
Tc cell = cellElement.getValue();
keys.add(Docx4jUtil.getTextFromCell(cell));
}
}
return keys;
}
/**
* 获取内容中需要填充的keys
*
* @param tempContent 标题下配置的内容
*/
public static List<List<String>> getFillKeys(List<HeadingContent> tempContent) {
List<List<String>> keys = new ArrayList<>();
List<String> pKeys = new ArrayList<>();
List<String> tableKeys = new ArrayList<>();
if (CollUtil.isNotEmpty(tempContent)) {
// 读取该表下模板里面的内容,这整个内容需要跟随误差范围循环的,确保内容的数据比较用的一个误差范围
Docx4jUtil.HeadingContent headingContent = tempContent.get(0);
for (Object object : headingContent.getSubContent()) {
if (object instanceof P) {
// 如果是段落,渲染段落内容
P paragraph = (P) object;
String textFromP = getTextFromP(paragraph);
String[] splitKeys = textFromP.split(StrPool.DASHED);
pKeys.addAll(Arrays.asList(splitKeys));
} else if (object instanceof JAXBElement) {
// 复制表格元素
JAXBElement<Tbl> copiedTableElement = (JAXBElement<Tbl>) object;
// 解析表格并插入对应数据,最关键的是得知道表格是横向还是纵向以及表头占了几行
Tbl tbl = copiedTableElement.getValue();
// 获取表格的行
List<Object> rows = tbl.getContent();
boolean isRow = Docx4jUtil.judgeTableCross(rows.get(0));
if (isRow) {
// 获取需要填的值的key
List<String> cellKeys = Docx4jUtil.getTableKey((Tr) rows.get(rows.size() - 1));
tableKeys.addAll(cellKeys);
} else {
// 纵向表格暂不考虑
}
}
}
}
keys.add(pKeys);
keys.add(tableKeys);
return keys;
}
/**
* 根据已知信息创建新航
*
* @param factory 工厂
* @param valueMap 数据
* @param tableKeys keys
* @param trPr 行样式
* @param tcPr 单元格样式
*/
public static Tr createCustomRow(ObjectFactory factory, Map<String, String> valueMap, List<String> tableKeys, TrPr trPr, TcPr tcPr, boolean centerFlag) {
Tr row = factory.createTr();
for (String tableKey : tableKeys) {
Tc cell = factory.createTc();
P paragraph = factory.createP();
R run = factory.createR();
String value = valueMap.get(tableKey);
Text text = factory.createText();
text.setValue(value);
run.getContent().add(text);
paragraph.getContent().add(run);
// 设置段落居中
if (centerFlag) {
PPr pPr = factory.createPPr();
Jc jc = factory.createJc();
jc.setVal(JcEnumeration.CENTER);
pPr.setJc(jc);
paragraph.setPPr(pPr);
}
if (value.equals("不合格")) {
RPr rPr = factory.createRPr();
Color color = factory.createColor();
// 红色
color.setVal("FF0000");
rPr.setColor(color);
run.setRPr(rPr);
}
cell.getContent().add(paragraph);
cell.setTcPr(tcPr);
row.getContent().add(cell);
row.setTrPr(trPr);
}
return row;
}
/**
* 根据已知信息创建新行
*
* @param factory 工厂
* @param cellValues 数据
* @param ascFontStyle 西文字体
* @param eastFontStyle 中文字体
* @param size 字体大小
* @param boldFlag 是否加粗
* @param centerFlag 是否居中
*/
public static Tr createCustomRow(ObjectFactory factory, List<String> cellValues, String ascFontStyle,String eastFontStyle, Integer size, boolean boldFlag, boolean centerFlag) {
Tr row = factory.createTr();
for (String value : cellValues) {
Tc cell = factory.createTc();
P paragraph = factory.createP();
R run = factory.createR();
Text text = factory.createText();
text.setValue(value);
run.getContent().add(text);
paragraph.getContent().add(run);
// 设置段落居中
if (centerFlag) {
PPr pPr = factory.createPPr();
Jc jc = factory.createJc();
jc.setVal(JcEnumeration.CENTER);
pPr.setJc(jc);
paragraph.setPPr(pPr);
}
RPr rPr = factory.createRPr();
if (value.equals("不合格")) {
Color color = factory.createColor();
// 红色
color.setVal("FF0000");
rPr.setColor(color);
}
if(boldFlag){
BooleanDefaultTrue bold = factory.createBooleanDefaultTrue();
rPr.setB(bold);
}
RFonts fonts = factory.createRFonts();
// 西文字体
fonts.setAscii(ascFontStyle);
// 中文字体
fonts.setEastAsia(eastFontStyle);
rPr.setRFonts(fonts);
HpsMeasure fontSize = factory.createHpsMeasure();
fontSize.setVal(BigInteger.valueOf(size));
// 西文字号
rPr.setSz(fontSize);
// 中文字号
rPr.setSzCs(fontSize);
run.setRPr(rPr);
cell.getContent().add(paragraph);
row.getContent().add(cell);
}
return row;
}
public static JAXBElement<Tbl> deepCopyTbl(JAXBElement<Tbl> original) throws Exception {
// 使用 docx4j 的 XmlUtils 进行深拷贝
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
Tbl clonedTbl = (Tbl) XmlUtils.deepCopy(original.getValue());
// 重新包装为 JAXBElement
return new JAXBElement<Tbl>(
original.getName(),
original.getDeclaredType(),
original.getScope(),
clonedTbl
);
}
// 存储Heading 5及其子内容的辅助类
public static class HeadingContent {
private String headingText;
private List<Object> subContent = new ArrayList<>();
public void setHeadingText(String text) {
this.headingText = text;
}
public String getHeadingText() {
return headingText;
}
public void addSubContent(Object obj) {
subContent.add(obj);
}
public List<Object> getSubContent() {
return subContent;
}
}
/**
* 获取段落在文档中的位置
*/
public static int getParagraphPosition(MainDocumentPart baseDocumentPart, DocAnchorEnum docAnchorEnum) {
List<Object> baseContent = baseDocumentPart.getContent();
for (int i = 0; i < baseContent.size(); i++) {
Object obj = baseContent.get(i);
if (obj instanceof P) {
P p = (P) obj;
String text = getTextFromP(p).trim();
if (text.startsWith(ReportConstant.BOOKMARK_START)) {
DocAnchorEnum anchorEnum = DocAnchorEnum.getByKey(text);
if (anchorEnum != null && anchorEnum.getKey().equalsIgnoreCase(docAnchorEnum.getKey())) {
return i;
}
}
}
}
return -1;
}
/**
* 获取表格样式
*/
public static TblPr getTblPr(ObjectFactory factory) {
// **关键步骤:设置黑色实线边框**
TblPr tblPr = factory.createTblPr();
TblBorders borders = factory.createTblBorders();
// 定义边框样式1磅黑色单实线
CTBorder border = new CTBorder();
// 实线类型
border.setVal(STBorder.SINGLE);
// 1磅=4单位1/8磅
border.setSz(BigInteger.valueOf(4));
// 黑色
border.setColor("000000");
// 应用边框到所有边
borders.setTop(border);
borders.setBottom(border);
borders.setLeft(border);
borders.setRight(border);
// 内部水平线
borders.setInsideH(border);
// 内部垂直线
borders.setInsideV(border);
tblPr.setTblBorders(borders);
// 设置表格宽度为96%
TblWidth tblWidth = factory.createTblWidth();
// 百分比类型
tblWidth.setType("pct");
// 96% = 4800/5000 (ISO标准)
tblWidth.setW(BigInteger.valueOf(5000));
tblPr.setTblW(tblWidth);
return tblPr;
}
}

View File

@@ -1,7 +1,13 @@
package com.njcn.gather.report.utils;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -45,54 +51,6 @@ public class WordUtil {
});
}
private static void insertPageBreak(XWPFDocument target) {
if(!isDocumentEmpty(target)){
// 获取最后一个页面的段落
XWPFParagraph pageBreakParagraph = getLastPageParagraph(target);
// 设置分页符
pageBreakParagraph.setPageBreak(true);
}
}
public static boolean isDocumentEmpty(XWPFDocument document) {
// 检查段落
List<XWPFParagraph> paragraphs = document.getParagraphs();
if (paragraphs != null && !paragraphs.isEmpty()) {
for (XWPFParagraph paragraph : paragraphs) {
if (paragraph.getText() != null && !paragraph.getText().trim().isEmpty()) {
return false;
}
}
}
// 检查表格
List<XWPFTable> tables = document.getTables();
if (tables != null && !tables.isEmpty()) {
return false;
}
return true;
}
public static XWPFParagraph getLastPageParagraph(XWPFDocument document) {
XWPFParagraph lastPageParagraph = null;
for (XWPFParagraph paragraph : document.getParagraphs()) {
lastPageParagraph = paragraph;
}
return lastPageParagraph;
}
private static boolean containsPageBreak(XWPFParagraph paragraph) {
for (XWPFRun run : paragraph.getRuns()) {
if (run.getText(0) != null && run.getText(0).contains("\f")) {
return true;
}
}
return false;
}
/**
* 替换表格中的占位符
@@ -145,6 +103,42 @@ public class WordUtil {
}
}
/**
* 替换文档中的占位符
* @param document 文档
* @param placeholders 待替换的占位符
*/
public static void replacePlaceholders(XWPFDocument document, Map<String, String> placeholders) {
replacePlaceholdersInParagraphs(document,placeholders);
replacePlaceholdersInTables(document,placeholders);
}
public static List<XWPFParagraph> findHeadingLevel5Paragraphs(XWPFDocument document) {
List<XWPFParagraph> headingLevel5Paragraphs = new ArrayList<>();
for (XWPFParagraph paragraph : document.getParagraphs()) {
String style = paragraph.getStyle();
if ("5".equals(style)) {
headingLevel5Paragraphs.add(paragraph);
}
}
return headingLevel5Paragraphs;
}
/**
* 获取段落的位置通过遍历bodyElements
*/
public static int getBodyElementPosition(XWPFDocument document, XWPFParagraph paragraph) {
List<IBodyElement> bodyElements = document.getBodyElements();
for (int i = 0; i < bodyElements.size(); i++) {
if (bodyElements.get(i) instanceof XWPFParagraph && bodyElements.get(i).equals(paragraph)) {
return i;
}
}
return -1;
}
}

View File

@@ -1,11 +1,14 @@
package com.njcn.gather.result.service;
import com.njcn.gather.report.pojo.result.SingleTestResult;
import com.njcn.gather.result.pojo.param.ResultParam;
import com.njcn.gather.result.pojo.vo.FormContentVO;
import com.njcn.gather.result.pojo.vo.ResultVO;
import com.njcn.gather.result.pojo.vo.TreeDataVO;
import com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO;
import java.util.List;
import java.util.Map;
/**
* @author caozehui
@@ -75,4 +78,22 @@ public interface IResultService {
* @param param
*/
void reCalculate(ResultParam.ChangeErrorSystemParam param);
/**
* 获取某测试大项的检测项内容
*
* @param checkDataVOList 检测项脚本
* @param planCode 计划编号
* @param devId 被检设备id
* @param lineNo 回路号
* @param tableKeys 表格key
*/
SingleTestResult getFinalContent(List<PqScriptDtlDataVO> checkDataVOList, String planCode, String devId, Integer lineNo, List<String> tableKeys);
/**
* 获取段落中指定的key对应的值
* @param itemCode 测试大项code
* @param pKeys 待填充的值
*/
Map<String, String> getParagraphKeysValue(String itemCode, List<String> pKeys);
}

View File

@@ -4,20 +4,30 @@ import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.PubUtils;
import com.njcn.gather.detection.pojo.enums.DetectionCodeEnum;
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
import com.njcn.gather.detection.pojo.po.DevData;
import com.njcn.gather.detection.pojo.vo.DetectionData;
import com.njcn.gather.detection.service.impl.DetectionServiceImpl;
import com.njcn.gather.device.service.IPqDevService;
import com.njcn.gather.plan.pojo.po.AdPlan;
import com.njcn.gather.plan.service.IAdPlanService;
import com.njcn.gather.pojo.enums.DetectionResponseEnum;
import com.njcn.gather.report.pojo.constant.PowerConstant;
import com.njcn.gather.report.pojo.enums.AffectEnum;
import com.njcn.gather.report.pojo.enums.ItemReportKeyEnum;
import com.njcn.gather.report.pojo.enums.PowerIndexEnum;
import com.njcn.gather.report.pojo.result.SingleTestResult;
import com.njcn.gather.result.pojo.enums.ResultUnitEnum;
import com.njcn.gather.result.pojo.param.ResultParam;
import com.njcn.gather.result.pojo.vo.FormContentVO;
@@ -30,10 +40,12 @@ import com.njcn.gather.script.pojo.param.PqScriptIssueParam;
import com.njcn.gather.script.pojo.po.PqScriptCheckData;
import com.njcn.gather.script.pojo.po.PqScriptDtls;
import com.njcn.gather.script.pojo.po.SourceIssue;
import com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO;
import com.njcn.gather.script.service.IPqScriptCheckDataService;
import com.njcn.gather.script.service.IPqScriptDtlsService;
import com.njcn.gather.script.util.ScriptDtlsDesc;
import com.njcn.gather.storage.mapper.TableGenMapper;
import com.njcn.gather.storage.pojo.param.SingleNonHarmParam;
import com.njcn.gather.storage.pojo.param.StorageParam;
import com.njcn.gather.storage.pojo.po.AdBaseResult;
import com.njcn.gather.storage.pojo.po.AdHarmonicResult;
@@ -910,6 +922,528 @@ public class ResultServiceImpl implements IResultService {
this.calculateResult(param.getPlanId(), param.getScriptId(), param.getCode(), param.getErrorSysId(), param.getDeviceId());
}
/**
* 获取某测试大项的检测项内容,主要针对表格需要填充的值
* 注:虽然测试项区是三相,他们的标准值可能不同,但是误差范围是一致的
* 1、区分额定条件、单影响量条件、单影响量条件
* 2、区分scriptIndex
* 3、区分谐波次数有必要的情况下
*
* @param dtlDataVOList 检测项脚本
* @param planCode 计划编号
* @param devId 被检设备id
* @param lineNo 回路号
* @param tableKeys 表格key
*/
@Override
public SingleTestResult getFinalContent(List<PqScriptDtlDataVO> dtlDataVOList, String planCode, String devId, Integer lineNo, List<String> tableKeys) {
SingleTestResult singleTestResult = new SingleTestResult();
Map<String/*subType影响量额定或某单影响量*/, List<Map<String/*误差范围*/, List<Map<String/*填充key*/, String/*实际值*/>>>>> finalContent = new HashMap<>();
if (CollUtil.isNotEmpty(dtlDataVOList)) {
// 首先区分测试条件
Map<String, List<PqScriptDtlDataVO>> subTypeMap = dtlDataVOList.stream().collect(Collectors.groupingBy(PqScriptDtlDataVO::getScriptSubType));
subTypeMap.forEach((subType, scriptDtlDataVOList) -> {
AffectEnum affectEnum = AffectEnum.getByKey(subType);
if (Objects.nonNull(affectEnum)) {
String scriptCode = scriptDtlDataVOList.get(0).getScriptCode();
String scriptId = scriptDtlDataVOList.get(0).getScriptId();
PowerIndexEnum indexEnum = PowerIndexEnum.getByKey(scriptCode);
if (Objects.nonNull(indexEnum)) {
String affectName = affectEnum.getDesc().replaceAll("XX", indexEnum.getDesc());
// 同一个影响量下,获取出子脚本信息
List<Integer> indexList = scriptDtlDataVOList.stream().map(PqScriptDtlDataVO::getScriptIndex).distinct().collect(Collectors.toList());
List<PqScriptCheckData> scriptCheckDataList = pqScriptCheckDataService.listCheckData(scriptId, indexList);
List<String> valueTypeList = scriptCheckDataList.stream().map(PqScriptCheckData::getValueType).distinct().collect(Collectors.toList());
// 查询检测结果,区分下表。
if (PowerConstant.TIME.contains(scriptCode)) {
// 谐波的测试项肯定是三相的可能会存在多次的本处要填充的key全集为time、standard、standardA、standardB、standardC、testA、testB、testC、errorA、errorB、errorC、result
// 查询结果数据经过上层处理谐波类此处的scriptIndex确保只有一个
if (indexList.size() == 1 && valueTypeList.size() == 1) {
// 获取谐波数据
SingleNonHarmParam param = new SingleNonHarmParam(Integer.parseInt(planCode), devId, lineNo, valueTypeList.get(0), indexList.get(0));
AdHarmonicResult singleResult = adHarmonicService.getSingleResult(param);
// 注如果ABC的标准值一致则同步到standard中
Map<Double, List<PqScriptCheckData>> checkDataHarmNumMap = scriptCheckDataList.stream().collect(Collectors.groupingBy(PqScriptCheckData::getHarmNum));
List<Map<String, String>> keyFillMapList = new ArrayList<>();
checkDataHarmNumMap.forEach((harmNum, dtlsList) -> {
Map<String, String> keyFillMap = new HashMap<>();
// 次数需要区分谐波&间谐波
String time;
if (harmNum % 1 == 0) {
// 谐波,需要转为正数字符串
time = String.valueOf(harmNum.intValue());
} else {
// 间谐波,保留小数位转为字符串
time = String.format("%.1f", harmNum);
}
keyFillMap.put(ItemReportKeyEnum.TIME.getKey(), time);
// 将间谐波次数取整1.5取22.5取3
double timeDouble = Math.round(harmNum);
int timeInt = (int) timeDouble;
DetectionData tempA = getResultData(singleResult, timeInt, PowerConstant.PHASE_A);
DetectionData tempB = getResultData(singleResult, timeInt, PowerConstant.PHASE_B);
DetectionData tempC = getResultData(singleResult, timeInt, PowerConstant.PHASE_C);
// 待填充Key
String standard = "/", standardA = "/", standardB = "/", standardC = "/",
testA = "/", testB = "/", testC = "/",
errorA = "/", errorB = "/", errorC = "/",
resultA = "/", resultB = "/", resultC = "/", result = "/",
errorScope = "/", unit = "";
if (Objects.nonNull(tempA) && (PowerConstant.DATA_RANGE.contains(tempA.getIsData()))) {
standardA = PubUtils.doubleRoundStr(4, tempA.getResultData());
testA = PubUtils.doubleRoundStr(4, tempA.getData());
errorA = PubUtils.doubleRoundStr(4, tempA.getErrorData().doubleValue());
resultA = tempA.getIsData() == 1 ? "合格" : "不合格";
errorScope = tempA.getRadius();
unit = tempA.getUnit();
standard = PubUtils.doubleRoundStr(4, tempA.getResultData());
}
if (Objects.nonNull(tempB) && (PowerConstant.DATA_RANGE.contains(tempB.getIsData()))) {
standardB = PubUtils.doubleRoundStr(4, tempB.getResultData());
testB = PubUtils.doubleRoundStr(4, tempB.getData());
errorB = PubUtils.doubleRoundStr(4, tempB.getErrorData().doubleValue());
resultB = tempB.getIsData() == 1 ? "合格" : "不合格";
if (errorScope.equals("/")) {
errorScope = tempB.getRadius();
}
if (StrUtil.isBlank(unit)) {
unit = tempB.getUnit();
}
if (standard.equals("/")) {
standard = PubUtils.doubleRoundStr(4, tempB.getResultData());
}
}
if (Objects.nonNull(tempC) && (PowerConstant.DATA_RANGE.contains(tempC.getIsData()))) {
standardC = PubUtils.doubleRoundStr(4, tempC.getResultData());
testC = PubUtils.doubleRoundStr(4, tempC.getData());
errorC = PubUtils.doubleRoundStr(4, tempC.getErrorData().doubleValue());
resultC = tempC.getIsData() == 1 ? "合格" : "不合格";
if (errorScope.equals("/")) {
errorScope = tempC.getRadius();
}
if (StrUtil.isBlank(unit)) {
unit = tempC.getUnit();
}
if (standard.equals("/")) {
standard = PubUtils.doubleRoundStr(4, tempC.getResultData());
}
}
if (standardA.equals(standardB) && standardA.equals(standardC)) {
standard = standardA;
}
// 标准值
keyFillMap.put(ItemReportKeyEnum.STANDARD.getKey(), standard);
keyFillMap.put(ItemReportKeyEnum.STANDARD_A.getKey(), standardA);
keyFillMap.put(ItemReportKeyEnum.STANDARD_B.getKey(), standardB);
keyFillMap.put(ItemReportKeyEnum.STANDARD_C.getKey(), standardC);
// 测试值
keyFillMap.put(ItemReportKeyEnum.TEST_A.getKey(), testA);
keyFillMap.put(ItemReportKeyEnum.TEST_B.getKey(), testB);
keyFillMap.put(ItemReportKeyEnum.TEST_C.getKey(), testC);
// 误差值
keyFillMap.put(ItemReportKeyEnum.ERROR_A.getKey(), errorA);
keyFillMap.put(ItemReportKeyEnum.ERROR_B.getKey(), errorB);
keyFillMap.put(ItemReportKeyEnum.ERROR_C.getKey(), errorC);
// 结果
keyFillMap.put(ItemReportKeyEnum.RESULT_A.getKey(), resultA);
keyFillMap.put(ItemReportKeyEnum.RESULT_B.getKey(), resultB);
keyFillMap.put(ItemReportKeyEnum.RESULT_C.getKey(), resultC);
if (resultA.equals("不合格") || resultB.equals("不合格") || resultC.equals("不合格")) {
result = "不合格";
} else if (!resultA.equals("/") || !resultB.equals("/") || !resultC.equals("/")) {
result = "合格";
}
keyFillMap.put(ItemReportKeyEnum.RESULT.getKey(), result);
errorScope = dealErrorScope(errorScope).concat(unit);
keyFillMap.put(ItemReportKeyEnum.ERROR_SCOPE.getKey(), errorScope);
keyFillMapList.add(keyFillMap);
});
// 按次数排序
PubUtils.sortByDoubleValue(keyFillMapList, ItemReportKeyEnum.TIME.getKey());
// 取出任意一次谐波数据的误差范围作为key
String titleScope = keyFillMapList.get(0).get(ItemReportKeyEnum.ERROR_SCOPE.getKey());
Map<String, List<Map<String, String>>> errorScoperMap = new HashMap<>();
errorScoperMap.put(titleScope, keyFillMapList);
List<Map<String, List<Map<String, String>>>> errorScoperMapList = new ArrayList<>();
errorScoperMapList.add(errorScoperMap);
finalContent.put(affectName, errorScoperMapList);
} else {
log.error("生成谐波类表格数据失败,脚本配置不支持,请核实。");
throw new BusinessException("生成谐波类表格数据失败,脚本配置不支持,请核实。");
}
} else {
// 非谐波的需要区分是否为ABC相还是T相
if (PowerConstant.THREE_PHASE.contains(scriptCode)) {
if (valueTypeList.size() == 1) {
// 获取该三相的数据
SingleNonHarmParam param = new SingleNonHarmParam(Integer.parseInt(planCode), devId, lineNo, valueTypeList, indexList);
List<AdNonHarmonicResult> nonHarmList = adNonHarmonicService.queryByCondition(param);
// 三相的数据通常包含standard、standardA、standardB、standardC、testA、testB、testC、errorA、errorB、errorC、resultA、resultB、resultC、result、errorScope
if (CollUtil.isNotEmpty(nonHarmList)) {
List<Map<String, String>> keyFillMapList = new ArrayList<>();
for (AdNonHarmonicResult adNonHarmonicResult : nonHarmList) {
Map<String, String> keyFillMap = new HashMap<>(16);
DetectionData tempA = getResultData(adNonHarmonicResult, null, PowerConstant.PHASE_A);
DetectionData tempB = getResultData(adNonHarmonicResult, null, PowerConstant.PHASE_B);
DetectionData tempC = getResultData(adNonHarmonicResult, null, PowerConstant.PHASE_C);
// 待填充Key
String standard = "/", standardA = "/", standardB = "/", standardC = "/",
testA = "/", testB = "/", testC = "/",
errorA = "/", errorB = "/", errorC = "/",
resultA = "/", resultB = "/", resultC = "/", result = "/",
errorScope = "/", unit = "";
if (Objects.nonNull(tempA) && (PowerConstant.DATA_RANGE.contains(tempA.getIsData()))) {
standardA = PubUtils.doubleRoundStr(4, tempA.getResultData());
testA = PubUtils.doubleRoundStr(4, tempA.getData());
errorA = PubUtils.doubleRoundStr(4, tempA.getErrorData().doubleValue());
resultA = tempA.getIsData() == 1 ? "合格" : "不合格";
errorScope = tempA.getRadius();
unit = tempA.getUnit();
standard = PubUtils.doubleRoundStr(4, tempA.getResultData());
}
if (Objects.nonNull(tempB) && (PowerConstant.DATA_RANGE.contains(tempB.getIsData()))) {
standardB = PubUtils.doubleRoundStr(4, tempB.getResultData());
testB = PubUtils.doubleRoundStr(4, tempB.getData());
errorB = PubUtils.doubleRoundStr(4, tempB.getErrorData().doubleValue());
resultB = tempB.getIsData() == 1 ? "合格" : "不合格";
if (errorScope.equals("/")) {
errorScope = tempB.getRadius();
}
if (StrUtil.isBlank(unit)) {
unit = tempB.getUnit();
}
if (standard.equals("/")) {
standard = PubUtils.doubleRoundStr(4, tempB.getResultData());
}
}
if (Objects.nonNull(tempC) && (PowerConstant.DATA_RANGE.contains(tempC.getIsData()))) {
standardC = PubUtils.doubleRoundStr(4, tempC.getResultData());
testC = PubUtils.doubleRoundStr(4, tempC.getData());
errorC = PubUtils.doubleRoundStr(4, tempC.getErrorData().doubleValue());
resultC = tempC.getIsData() == 1 ? "合格" : "不合格";
if (errorScope.equals("/")) {
errorScope = tempC.getRadius();
}
if (StrUtil.isBlank(unit)) {
unit = tempC.getUnit();
}
if (standard.equals("/")) {
standard = PubUtils.doubleRoundStr(4, tempC.getResultData());
}
}
if (standardA.equals(standardB) && standardA.equals(standardC)) {
standard = standardA;
}
// 标准值
keyFillMap.put(ItemReportKeyEnum.STANDARD.getKey(), standard);
keyFillMap.put(ItemReportKeyEnum.STANDARD_A.getKey(), standardA);
keyFillMap.put(ItemReportKeyEnum.STANDARD_B.getKey(), standardB);
keyFillMap.put(ItemReportKeyEnum.STANDARD_C.getKey(), standardC);
// 测试值
keyFillMap.put(ItemReportKeyEnum.TEST_A.getKey(), testA);
keyFillMap.put(ItemReportKeyEnum.TEST_B.getKey(), testB);
keyFillMap.put(ItemReportKeyEnum.TEST_C.getKey(), testC);
// 误差值
keyFillMap.put(ItemReportKeyEnum.ERROR_A.getKey(), errorA);
keyFillMap.put(ItemReportKeyEnum.ERROR_B.getKey(), errorB);
keyFillMap.put(ItemReportKeyEnum.ERROR_C.getKey(), errorC);
// 结果
keyFillMap.put(ItemReportKeyEnum.RESULT_A.getKey(), resultA);
keyFillMap.put(ItemReportKeyEnum.RESULT_B.getKey(), resultB);
keyFillMap.put(ItemReportKeyEnum.RESULT_C.getKey(), resultC);
if (resultA.equals("不合格") || resultB.equals("不合格") || resultC.equals("不合格")) {
result = "不合格";
} else if (!resultA.equals("/") || !resultB.equals("/") || !resultC.equals("/")) {
result = "合格";
}
keyFillMap.put(ItemReportKeyEnum.RESULT.getKey(), result);
errorScope = dealErrorScope(errorScope).concat(unit);
keyFillMap.put(ItemReportKeyEnum.ERROR_SCOPE.getKey(), errorScope);
keyFillMapList.add(keyFillMap);
}
// 需要对所有的填充进行按误差范围分组
Map<String, List<Map<String, String>>> errorScoperMap = keyFillMapList.stream()
.collect(Collectors.groupingBy(map -> map.get(ItemReportKeyEnum.ERROR_SCOPE.getKey())));
// 分组后,还需要针对标准值进行一个升序
errorScoperMap.forEach((errorScope, maps) -> {
PubUtils.sortByDoubleValue(maps, ItemReportKeyEnum.STANDARD.getKey());
});
List<Map<String, List<Map<String, String>>>> errorList = new ArrayList<>();
errorList.add(errorScoperMap);
// 最后赋值返回
finalContent.put(affectName, errorList);
} else {
log.error("生成三相类表格数据失败,结果表数据丢失,请核实。");
}
} else {
log.error("生成三相类表格数据失败,脚本配置不支持,请核实。");
throw new BusinessException("生成三相类表格数据失败,脚本配置不支持,请核实。");
}
} else {
// 非三相的还需要特殊处理下暂态的
if (PowerConstant.VOLTAGE.equalsIgnoreCase(scriptCode)) {
// 暂态的valueType通常只有2个一个特征幅值一个持续时间
List<Map<String, String>> keyFillMapList = new ArrayList<>();
for (Integer sort : indexList) {
SingleNonHarmParam param = new SingleNonHarmParam(Integer.parseInt(planCode), devId, lineNo, valueTypeList, Collections.singletonList(sort));
List<AdNonHarmonicResult> nonHarmList = adNonHarmonicService.queryByCondition(param);
// 暂态的数据通常包含standardMag、standardDur、testMag、testDur、errorMag、errorDur、resultMag、resultDur、result、errorScope、errorScopeMag、errorScopeDur
if (CollUtil.isNotEmpty(nonHarmList)) {
String standardMag = "/", standardDur = "/",
testMag = "/", testDur = "/",
errorMag = "/", errorDur = "/",
resultMag = "/", resultDur = "/", result,
errorScope, errorScopeMag = "/", errorScopeDur = "/",
unitMag = "", unitDur = "";
Map<String, String> keyFillMap = new HashMap<>(16);
for (AdNonHarmonicResult adNonHarmonicResult : nonHarmList) {
DetectionData tempT = getResultData(adNonHarmonicResult, null, PowerConstant.PHASE_T);
// 需要判断adNonHarmonicResult是特征幅值还是持续时间
String adType = adNonHarmonicResult.getAdType();
DictTree temp = dictTreeService.getById(adType);
if (temp.getCode().equalsIgnoreCase("MAG")) {
// 特征幅值
if (Objects.nonNull(tempT) && PowerConstant.DATA_RANGE.contains(tempT.getIsData())) {
standardMag = PubUtils.doubleRoundStr(4, tempT.getResultData());
testMag = PubUtils.doubleRoundStr(4, tempT.getData());
errorMag = PubUtils.doubleRoundStr(4, tempT.getErrorData().doubleValue());
resultMag = tempT.getIsData() == 1 ? "合格" : "不合格";
unitMag = tempT.getUnit();
errorScopeMag = tempT.getRadius();
}
} else if (temp.getCode().equalsIgnoreCase("DUR")) {
// 持续时间,需要注意时间单位处理,默认是秒
String timeUnit = "s";
for (String tableKey : tableKeys) {
if (tableKey.contains(ItemReportKeyEnum.STANDARD_DUR.getKey())) {
//截取单位
String[] tempStr = tableKey.split(StrPool.UNDERLINE);
if (tempStr.length > 1) {
if (tempStr[1].equalsIgnoreCase("ms")) {
timeUnit = "ms";
}
}
}
}
standardDur = PubUtils.doubleRoundStr(4, tempT.getResultData());
testDur = PubUtils.doubleRoundStr(4, tempT.getData());
errorDur = PubUtils.doubleRoundStr(4, tempT.getErrorData().doubleValue());
resultDur = tempT.getIsData() == 1 ? "合格" : "不合格";
unitDur = tempT.getUnit();
errorScopeDur = tempT.getRadius();
if (timeUnit.equalsIgnoreCase("ms")) {
// 如果是ms上述的一些数据需要重新处理
if (!standardDur.equalsIgnoreCase("/")) {
standardDur = PubUtils.doubleRoundStr(4, Double.parseDouble(standardDur) * 1000);
}
if (!testDur.equalsIgnoreCase("/")) {
testDur = PubUtils.doubleRoundStr(4, Double.parseDouble(testDur) * 1000);
}
if (!errorDur.equalsIgnoreCase("/")) {
errorDur = PubUtils.doubleRoundStr(4, Double.parseDouble(errorDur) * 1000);
}
if(!errorScopeDur.equalsIgnoreCase("/")){
if(errorScopeDur.contains("~")){
String[] tempStr = errorScopeDur.split("~");
errorScopeDur = PubUtils.doubleRoundStr(0, Double.parseDouble(tempStr[0]) * 1000).concat("~").concat(PubUtils.doubleRoundStr(0, Double.parseDouble(tempStr[1]) * 1000));
}
}
unitDur = "ms";
}
}
}
errorScopeMag = dealErrorScope(errorScopeMag).concat(unitMag);
errorScopeDur = dealErrorScope(errorScopeDur).concat(unitDur);
errorScope = "特征幅值:".concat(errorScopeMag).concat(StrPool.COMMA).concat("持续时间:").concat(errorScopeDur);
keyFillMap.put(ItemReportKeyEnum.ERROR_SCOPE.getKey(), errorScope);
// 标准值
keyFillMap.put(ItemReportKeyEnum.STANDARD_MAG.getKey(), standardMag);
keyFillMap.put(ItemReportKeyEnum.STANDARD_DUR.getKey(), standardDur);
// 测试值
keyFillMap.put(ItemReportKeyEnum.TEST_MAG.getKey(), testMag);
keyFillMap.put(ItemReportKeyEnum.TEST_DUR.getKey(), testDur);
// 误差
keyFillMap.put(ItemReportKeyEnum.ERROR_MAG.getKey(), errorMag);
keyFillMap.put(ItemReportKeyEnum.ERROR_DUR.getKey(), errorDur);
// 结果
keyFillMap.put(ItemReportKeyEnum.RESULT_MAG.getKey(), resultMag);
keyFillMap.put(ItemReportKeyEnum.RESULT_DUR.getKey(), resultDur);
// 综合结果
result = resultMag;
if (!resultDur.equals("/") && result.equals("合格")) {
if (resultDur.equals("不合格")) {
result = "不合格";
}
}
keyFillMap.put(ItemReportKeyEnum.RESULT.getKey(), result);
keyFillMapList.add(keyFillMap);
}
}
// 需要对所有填充进行按误差范围分组
Map<String, List<Map<String, String>>> errorScoperMap = keyFillMapList.stream()
.collect(Collectors.groupingBy(map -> map.get(ItemReportKeyEnum.ERROR_SCOPE.getKey())));
// 分组后,还需要针对特征幅值标准值进行一个升序
errorScoperMap.forEach((errorScope, maps) -> {
PubUtils.sortByDoubleValue(maps, ItemReportKeyEnum.STANDARD_MAG.getKey());
});
List<Map<String, List<Map<String, String>>>> errorList = new ArrayList<>();
errorList.add(errorScoperMap);
// 最后赋值返回
finalContent.put(affectName, errorList);
} else {
// 非三相且非暂态,通常只有一个数据,所以直接赋值即可
List<Map<String, String>> keyFillMapList = new ArrayList<>();
SingleNonHarmParam param = new SingleNonHarmParam(Integer.parseInt(planCode), devId, lineNo, valueTypeList, indexList);
List<AdNonHarmonicResult> nonHarmList = adNonHarmonicService.queryByCondition(param);
if (CollUtil.isNotEmpty(nonHarmList)) {
for (AdNonHarmonicResult adNonHarmonicResult : nonHarmList) {
String standard = "/", test = "/", error = "/", result = "/", errorScope = "/",unit = "";
Map<String, String> keyFillMap = new HashMap<>(8);
DetectionData tempT = getResultData(adNonHarmonicResult, null, PowerConstant.PHASE_T);
if (Objects.nonNull(tempT) && PowerConstant.DATA_RANGE.contains(tempT.getIsData())) {
standard = PubUtils.doubleRoundStr(4, tempT.getResultData());
test = PubUtils.doubleRoundStr(4, tempT.getData());
error = PubUtils.doubleRoundStr(4, tempT.getErrorData().doubleValue());
result = tempT.getIsData() == 1 ? "合格" : "不合格";
unit = tempT.getUnit();
errorScope = tempT.getRadius();
}
keyFillMap.put(ItemReportKeyEnum.STANDARD.getKey(), standard);
keyFillMap.put(ItemReportKeyEnum.TEST.getKey(), test);
keyFillMap.put(ItemReportKeyEnum.ERROR.getKey(), error);
keyFillMap.put(ItemReportKeyEnum.RESULT.getKey(), result);
errorScope = dealErrorScope(errorScope).concat(unit);
keyFillMap.put(ItemReportKeyEnum.ERROR_SCOPE.getKey(), errorScope);
keyFillMapList.add(keyFillMap);
}
}
// 需要对所有填充进行按误差范围分组
Map<String, List<Map<String, String>>> errorScoperMap = keyFillMapList.stream()
.collect(Collectors.groupingBy(map -> map.get(ItemReportKeyEnum.ERROR_SCOPE.getKey())));
// 分组后,还需要针对特征幅值标准值进行一个升序
errorScoperMap.forEach((errorScope, maps) -> {
PubUtils.sortByDoubleValue(maps, ItemReportKeyEnum.STANDARD.getKey());
});
List<Map<String, List<Map<String, String>>>> errorList = new ArrayList<>();
errorList.add(errorScoperMap);
// 最后赋值返回
finalContent.put(affectName, errorList);
}
}
}
} else {
log.error("未找到合适的脚本信息");
}
} else {
log.error("未找到合适的测量条件");
}
});
}
// 返回之前做下总结性判断
singleTestResult.setQualified(judgeQualified(finalContent));
singleTestResult.setDetail(finalContent);
return singleTestResult;
}
/**
* 处理下误差范围,如果正负数一致时调整为±的形式
* 数据库中一般形式为:-0.1155~0.1155
*
* @param errorScope 误差范围
*/
private String dealErrorScope(String errorScope) {
if (errorScope.contains("~")) {
String[] split = errorScope.split("~");
String begin = split[0];
if (begin.contains(StrPool.DASHED)) {
begin = begin.substring(1);
}
String end = split[1];
if (end.equalsIgnoreCase(begin)) {
return "±" + begin;
}
}
return errorScope;
}
/**
* 遍历所有的结果是否存在不合格但凡有一个不合格就返回false
*
* @param finalContent 最终结果
*/
private boolean judgeQualified(Map<String, List<Map<String, List<Map<String, String>>>>> finalContent) {
List<String> results = finalContent.values().parallelStream()
.flatMap(List::stream)
.flatMap(m -> m.values().stream())
.flatMap(List::stream)
.filter(map -> map.containsKey(ItemReportKeyEnum.RESULT.getKey()))
.map(map -> map.get(ItemReportKeyEnum.RESULT.getKey()))
.collect(Collectors.toList());
List<String> qualifiedList = results.stream().filter("不合格"::equals).collect(Collectors.toList());
return CollUtil.isEmpty(qualifiedList);
}
/**
* 根据谐波结果数据获取局的测试数据
*
* @param singleResult 谐波数据
* @param timeInt 次数
* @param phaseA 相别
*/
private DetectionData getResultData(Object singleResult, Integer timeInt, String phaseA) {
String fieldName = phaseA.toLowerCase().concat("Value");
if (Objects.nonNull(timeInt)) {
fieldName = fieldName.concat(String.valueOf(timeInt));
}
String filedValue;
try {
filedValue = (String) ReflectUtil.getFieldValue(singleResult, fieldName);
} catch (Exception exception) {
throw new BusinessException("获取对象字段属性失败");
}
return JSONUtil.toBean(filedValue, DetectionData.class);
}
/**
* 获取段落中指定的key对应的值目前主要为测试大项名称服务通过code匹配
*
* @param itemCode 测试大项code
* @param pKeys 待填充的值
*/
@Override
public Map<String, String> getParagraphKeysValue(String itemCode, List<String> pKeys) {
Map<String, String> map = new HashMap<>();
if (CollUtil.isNotEmpty(pKeys)) {
for (String pKey : pKeys) {
ItemReportKeyEnum reportKeyEnum = ItemReportKeyEnum.getByKey(pKey);
if (Objects.nonNull(reportKeyEnum)) {
if (reportKeyEnum.getKey().equals(ItemReportKeyEnum.NAME.getKey())) {
PowerIndexEnum indexEnum = PowerIndexEnum.getByKey(itemCode);
if (Objects.nonNull(indexEnum)) {
map.put(reportKeyEnum.getKey(), indexEnum.getDesc());
} else {
log.error("电能指标枚举未找到测试项");
}
} else if (reportKeyEnum.getKey().equals(ItemReportKeyEnum.NAME_DETAIL.getKey())) {
PowerIndexEnum indexEnum = PowerIndexEnum.getByKey(itemCode);
if (Objects.nonNull(indexEnum)) {
map.put(reportKeyEnum.getKey(), indexEnum.getDesc().concat("测量准确度"));
} else {
log.error("电能指标枚举未找到测试项");
}
}
} else {
log.error("段落枚举未找到占用符");
}
}
}
return map;
}
private Integer conform(Set<Integer> numbers) {
if (CollUtil.isNotEmpty(numbers)) {
if (numbers.size() > 1) {

View File

@@ -2,6 +2,10 @@ package com.njcn.gather.script.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.njcn.gather.script.pojo.po.PqScriptCheckData;
import com.njcn.gather.script.pojo.vo.PqScriptCheckDataVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Description:

View File

@@ -2,8 +2,12 @@ package com.njcn.gather.script.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.njcn.gather.script.pojo.po.PqScriptDtls;
import com.njcn.gather.script.pojo.vo.PqScriptCheckDataVO;
import com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author caozehui
* @date 2024-11-18
@@ -17,5 +21,7 @@ public interface PqScriptDtlsMapper extends MPJBaseMapper<PqScriptDtls> {
* @return
*/
Integer selectMaxIndex(@Param("scriptId") String scriptId);
List<PqScriptDtlDataVO> getScriptDtlsDataList(@Param("scriptId")String scriptId);
}

View File

@@ -2,6 +2,5 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.gather.script.mapper.PqScriptCheckDataMapper">
</mapper>

View File

@@ -10,5 +10,21 @@
WHERE Script_Id = #{scriptId}
</select>
<select id="getScriptDtlsDataList" resultType="com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO" >
SELECT
t2.CODE scriptCode,
t2.NAME scriptName,
t1.*
FROM
pq_script_dtls t1,
sys_dict_tree t2
WHERE
t1.Script_Type = t2.Id
AND t1.Script_Id = #{scriptId}
AND t1.ENABLE = 1
ORDER BY
t1.Script_Index ASC,t1.HarmNum ASC
</select>
</mapper>

View File

@@ -0,0 +1,24 @@
package com.njcn.gather.script.pojo.vo;
import com.njcn.gather.script.pojo.po.PqScriptCheckData;
import lombok.Data;
/**
* @author hongawen
* @version 1.0
* @data 2025/3/26 14:45
*/
@Data
public class PqScriptCheckDataVO extends PqScriptCheckData {
/**
* 脚本项名称
*/
private String scriptName;
/**
* 脚本项Code
*/
private String scriptCode;
}

View File

@@ -1,23 +1,23 @@
package com.njcn.gather.script.pojo.vo;
import com.njcn.gather.script.pojo.po.PqScriptDtls;
import lombok.Data;
import java.io.Serializable;
@Data
public class PqScriptDtlDataVO implements Serializable {
public class PqScriptDtlDataVO extends PqScriptDtls implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 这条检测脚本的序号
* 脚本项名称
*/
private String index;
private String scriptName;
/**
* 脚本项Code
*/
private String scriptCode;
}

View File

@@ -3,6 +3,7 @@ package com.njcn.gather.script.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.njcn.gather.script.pojo.param.PqScriptCheckDataParam;
import com.njcn.gather.script.pojo.po.PqScriptCheckData;
import com.njcn.gather.script.pojo.vo.PqScriptCheckDataVO;
import java.util.List;
import java.util.Map;
@@ -36,4 +37,11 @@ public interface IPqScriptCheckDataService extends IService<PqScriptCheckData> {
List<PqScriptCheckData> listCheckDataCode(PqScriptCheckDataParam param);
Double getCheckDataValue(PqScriptCheckDataParam param);
/**
* 查询条件范围内的参与比较的脚本详情
* @param scriptId 脚本id
* @param indexList 脚本下标集合
*/
List<PqScriptCheckData> listCheckData(String scriptId, List<Integer> indexList);
}

View File

@@ -9,6 +9,8 @@ import com.njcn.gather.script.pojo.param.ScriptParam;
import com.njcn.gather.script.pojo.po.PqScriptCheckData;
import com.njcn.gather.script.pojo.po.PqScriptDtls;
import com.njcn.gather.script.pojo.po.SourceIssue;
import com.njcn.gather.script.pojo.vo.PqScriptCheckDataVO;
import com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO;
import java.util.List;
import java.util.Map;
@@ -139,4 +141,9 @@ public interface IPqScriptDtlsService extends IService<PqScriptDtls> {
* @return
*/
Map<String, Object> getScriptToIcdCheckInfo(PreDetectionParam param);
/**
* 根据脚本id获取脚本详情数据
* @param scriptId 脚本id
*/
List<PqScriptDtlDataVO> getScriptDtlsDataList(String scriptId);
}

View File

@@ -13,6 +13,8 @@ import com.njcn.gather.detection.pojo.enums.DetectionResponseEnum;
import com.njcn.gather.script.mapper.PqScriptCheckDataMapper;
import com.njcn.gather.script.pojo.param.PqScriptCheckDataParam;
import com.njcn.gather.script.pojo.po.PqScriptCheckData;
import com.njcn.gather.script.pojo.po.PqScriptDtls;
import com.njcn.gather.script.pojo.vo.PqScriptCheckDataVO;
import com.njcn.gather.script.service.IPqScriptCheckDataService;
import com.njcn.gather.system.dictionary.mapper.DictTreeMapper;
import com.njcn.gather.system.dictionary.pojo.po.DictTree;
@@ -121,5 +123,16 @@ public class PqScriptCheckDataServiceImpl extends ServiceImpl<PqScriptCheckDataM
return null;
}
@Override
public List<PqScriptCheckData> listCheckData(String scriptId, List<Integer> indexList) {
LambdaQueryWrapper<PqScriptCheckData> queryWrapper = new LambdaQueryWrapper<PqScriptCheckData>()
.eq(PqScriptCheckData::getScriptId, scriptId)
.eq(PqScriptCheckData::getEnable, DataStateEnum.ENABLE.getCode())
.eq(PqScriptCheckData::getErrorFlag, 1)
.in(PqScriptCheckData::getScriptIndex, indexList)
.orderByAsc(PqScriptCheckData::getHarmNum);
return this.list(queryWrapper);
}
}

View File

@@ -29,6 +29,8 @@ import com.njcn.gather.script.pojo.po.PqScript;
import com.njcn.gather.script.pojo.po.PqScriptCheckData;
import com.njcn.gather.script.pojo.po.PqScriptDtls;
import com.njcn.gather.script.pojo.po.SourceIssue;
import com.njcn.gather.script.pojo.vo.PqScriptCheckDataVO;
import com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO;
import com.njcn.gather.script.service.IPqScriptCheckDataService;
import com.njcn.gather.script.service.IPqScriptDtlsService;
import com.njcn.gather.script.util.ScriptDtlsDesc;
@@ -739,6 +741,11 @@ public class PqScriptDtlsServiceImpl extends ServiceImpl<PqScriptDtlsMapper, PqS
return map;
}
@Override
public List<PqScriptDtlDataVO> getScriptDtlsDataList(String scriptId) {
return this.baseMapper.getScriptDtlsDataList(scriptId);
}
private void unbanCheck(List<PqScriptDtlsParam.CheckData> info,
PqScriptDtlsParam.CheckData channelListDTO,
List<PqScriptDtlsParam.ChannelListDTO> list,

View File

@@ -0,0 +1,105 @@
package com.njcn.gather.advice;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.gather.system.log.service.ISysLogAuditService;
import com.njcn.web.utils.HttpServletUtil;
import com.njcn.web.utils.ReflectCommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.annotation.Nonnull;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
/**
* @author caozehui
* @data 2024-12-2
*/
@Slf4j
@ControllerAdvice
public class LogAdvice implements ResponseBodyAdvice<Object> {
@Autowired
private ISysLogAuditService logService;
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, 8, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
// 队列满时由主线程执行
new ThreadPoolExecutor.CallerRunsPolicy()
);
/**
* 无需审计记录的操作,比如根据客户端获取客户端信息
*/
private final static List<String> UN_LOG_INFO = Collections.singletonList("未知业务");
/**
* controller返回的响应体包含的状态码而非全局异常捕获器处抛出来的状态码
*/
private final static List<String> FILTER_CODE = Arrays.asList(CommonResponseEnum.SUCCESS.getCode(), CommonResponseEnum.FAIL.getCode(), CommonResponseEnum.NO_DATA.getCode());
/**
* 判断下结果,是不是成功
*
* @param returnType 返回类型包含大量信息controller、method、result等等
* @param converterType 消息转换器类型目前配置的是Jackson
*/
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
/**
* 拦截所有请求成功的操作进行日志入库处理
* 需要 用户标识、事件描述、事件结果、操作IP、事件类型、事件严重度、操作时间、操作类型
*
* @param body .
* @param returnType .
* @param selectedContentType .
* @param selectedConverterType .
* @param request .
* @param response .
* @return .
*/
@Override
public Object beforeBodyWrite(Object body, @Nonnull MethodParameter returnType, @Nonnull MediaType selectedContentType, @Nonnull Class selectedConverterType, @Nonnull ServerHttpRequest request, @Nonnull ServerHttpResponse response) {
if (body instanceof HttpResult) {
HttpResult<?> httpResult = (HttpResult<?>) body;
if (FILTER_CODE.contains(httpResult.getCode())) {
// 传递上下文
HttpServletRequest httpServletRequest = HttpServletUtil.getRequest();
String methodDescribe = ReflectCommonUtil.getMethodDescribeByMethod(returnType.getMethod());
if (!UN_LOG_INFO.contains(methodDescribe)) {
Future<?> future = executor.submit(() -> {
HttpServletUtil.setRequest(httpServletRequest);
logService.recodeAdviceLog(returnType, httpResult, methodDescribe);
});
try {
// 抛出 ExecutionException
future.get();
} catch (ExecutionException | InterruptedException e) {
log.error("保存审计日志异常,异常为:"+e.getMessage());
}
}
}
}
return body;
}
}

View File

@@ -6,11 +6,14 @@ 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.common.utils.LogUtil;
import com.njcn.gather.system.log.service.ISysLogAuditService;
import com.njcn.web.utils.HttpResultUtil;
import com.njcn.web.utils.HttpServletUtil;
import com.njcn.web.utils.ReflectCommonUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -18,6 +21,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.util.NestedServletException;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
@@ -25,6 +29,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -40,7 +45,16 @@ import java.util.stream.Stream;
@RestControllerAdvice
public class GlobalBusinessExceptionHandler {
// private final ILogService logService;
@Resource
private final ISysLogAuditService sysLogAuditService;
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, 8, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
// 队列满时由主线程执行
new ThreadPoolExecutor.CallerRunsPolicy()
);
/**
* 捕获业务功能异常,通常为业务数据抛出的异常
@@ -48,16 +62,9 @@ public class GlobalBusinessExceptionHandler {
* @param businessException 业务异常
*/
@ExceptionHandler(BusinessException.class)
public HttpResult<String> handleBusinessException(HttpServletRequest httpServletRequest, BusinessException businessException) {
public HttpResult<String> handleBusinessException(BusinessException businessException) {
String operate = ReflectCommonUtil.getMethodDescribeByException(businessException);
// logService.recodeBusinessExceptionLog(businessException, httpServletRequest, businessException.getMessage());
//判断方法上是否有自定义注解,做特殊处理
Method method = ReflectCommonUtil.getMethod(businessException);
// if (!Objects.isNull(method)){
// if(method.isAnnotationPresent(ReturnMsg.class)){
// return HttpResultUtil.assembleResult(businessException.getCode(), null, StrFormatter.format("{}",businessException.getMessage()));
// }
// }
recodeAdviceLog(businessException, businessException.getMessage());
return HttpResultUtil.assembleBusinessExceptionResult(businessException, null, operate);
}
@@ -68,9 +75,9 @@ public class GlobalBusinessExceptionHandler {
* @param nullPointerException 空指针异常
*/
@ExceptionHandler(NullPointerException.class)
public HttpResult<String> handleNullPointerException(HttpServletRequest httpServletRequest, NullPointerException nullPointerException) {
public HttpResult<String> handleNullPointerException(NullPointerException nullPointerException) {
LogUtil.logExceptionStackInfo(CommonResponseEnum.NULL_POINTER_EXCEPTION.getMessage(), nullPointerException);
// logService.recodeBusinessExceptionLog(nullPointerException, httpServletRequest, CommonResponseEnum.NULL_POINTER_EXCEPTION.getMessage());
recodeAdviceLog(nullPointerException, CommonResponseEnum.NULL_POINTER_EXCEPTION.getMessage());
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.NULL_POINTER_EXCEPTION, null, ReflectCommonUtil.getMethodDescribeByException(nullPointerException));
}
@@ -80,9 +87,9 @@ public class GlobalBusinessExceptionHandler {
* @param arithmeticException 算数运算异常由于除数为0引起的异常
*/
@ExceptionHandler(ArithmeticException.class)
public HttpResult<String> handleArithmeticException(HttpServletRequest httpServletRequest, ArithmeticException arithmeticException) {
public HttpResult<String> handleArithmeticException(ArithmeticException arithmeticException) {
LogUtil.logExceptionStackInfo(CommonResponseEnum.ARITHMETIC_EXCEPTION.getMessage(), arithmeticException);
// logService.recodeBusinessExceptionLog(arithmeticException, httpServletRequest, CommonResponseEnum.ARITHMETIC_EXCEPTION.getMessage());
recodeAdviceLog(arithmeticException, CommonResponseEnum.ARITHMETIC_EXCEPTION.getMessage());
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.ARITHMETIC_EXCEPTION, null, ReflectCommonUtil.getMethodDescribeByException(arithmeticException));
}
@@ -92,9 +99,9 @@ public class GlobalBusinessExceptionHandler {
* @param classCastException 类型转换异常
*/
@ExceptionHandler(ClassCastException.class)
public HttpResult<String> handleClassCastException(HttpServletRequest httpServletRequest, ClassCastException classCastException) {
public HttpResult<String> handleClassCastException(ClassCastException classCastException) {
LogUtil.logExceptionStackInfo(CommonResponseEnum.CLASS_CAST_EXCEPTION.getMessage(), classCastException);
// logService.recodeBusinessExceptionLog(classCastException, httpServletRequest, CommonResponseEnum.CLASS_CAST_EXCEPTION.getMessage());
recodeAdviceLog(classCastException, CommonResponseEnum.CLASS_CAST_EXCEPTION.getMessage());
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.CLASS_CAST_EXCEPTION, null, ReflectCommonUtil.getMethodDescribeByException(classCastException));
}
@@ -105,9 +112,9 @@ public class GlobalBusinessExceptionHandler {
* @param indexOutOfBoundsException 索引下标越界异常
*/
@ExceptionHandler(IndexOutOfBoundsException.class)
public HttpResult<String> handleIndexOutOfBoundsException(HttpServletRequest httpServletRequest, IndexOutOfBoundsException indexOutOfBoundsException) {
public HttpResult<String> handleIndexOutOfBoundsException(IndexOutOfBoundsException indexOutOfBoundsException) {
LogUtil.logExceptionStackInfo(CommonResponseEnum.INDEX_OUT_OF_BOUNDS_EXCEPTION.getMessage(), indexOutOfBoundsException);
// logService.recodeBusinessExceptionLog(indexOutOfBoundsException, httpServletRequest, CommonResponseEnum.INDEX_OUT_OF_BOUNDS_EXCEPTION.getMessage());
recodeAdviceLog(indexOutOfBoundsException, CommonResponseEnum.INDEX_OUT_OF_BOUNDS_EXCEPTION.getMessage());
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.INDEX_OUT_OF_BOUNDS_EXCEPTION, null, ReflectCommonUtil.getMethodDescribeByException(indexOutOfBoundsException));
}
@@ -117,10 +124,10 @@ public class GlobalBusinessExceptionHandler {
* @param httpMediaTypeNotSupportedException 请求中参数的媒体方式不支持异常
*/
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public HttpResult<String> httpMediaTypeNotSupportedExceptionHandler(HttpServletRequest httpServletRequest, HttpMediaTypeNotSupportedException httpMediaTypeNotSupportedException) {
public HttpResult<String> httpMediaTypeNotSupportedExceptionHandler(HttpMediaTypeNotSupportedException httpMediaTypeNotSupportedException) {
LogUtil.logExceptionStackInfo(CommonResponseEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED_EXCEPTION.getMessage(), httpMediaTypeNotSupportedException);
// 然后提取错误提示信息进行返回
// logService.recodeBusinessExceptionLog(httpMediaTypeNotSupportedException, httpServletRequest, CommonResponseEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED_EXCEPTION.getMessage());
recodeAdviceLog(httpMediaTypeNotSupportedException, CommonResponseEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED_EXCEPTION.getMessage());
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED_EXCEPTION, null, ReflectCommonUtil.getMethodDescribeByException(httpMediaTypeNotSupportedException));
}
@@ -131,13 +138,13 @@ public class GlobalBusinessExceptionHandler {
* @param methodArgumentNotValidException 参数校验异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public HttpResult<String> methodArgumentNotValidExceptionHandler(HttpServletRequest httpServletRequest, MethodArgumentNotValidException methodArgumentNotValidException) {
public HttpResult<String> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException methodArgumentNotValidException) {
// 从异常对象中拿到allErrors数据
String messages = methodArgumentNotValidException.getBindingResult().getAllErrors()
.stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(""));
// 然后提取错误提示信息进行返回
LogUtil.njcnDebug(log, "参数校验异常,异常为:{}", messages);
// logService.recodeBusinessExceptionLog(methodArgumentNotValidException, httpServletRequest, CommonResponseEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION.getMessage());
recodeAdviceLog(methodArgumentNotValidException, CommonResponseEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION.getMessage());
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION, messages, ControllerUtil.getMethodArgumentNotValidException(methodArgumentNotValidException));
}
@@ -148,7 +155,7 @@ public class GlobalBusinessExceptionHandler {
* @param constraintViolationException 参数校验异常
*/
@ExceptionHandler(ConstraintViolationException.class)
public HttpResult<String> constraintViolationExceptionExceptionHandler(HttpServletRequest httpServletRequest, ConstraintViolationException constraintViolationException) {
public HttpResult<String> constraintViolationExceptionExceptionHandler(ConstraintViolationException constraintViolationException) {
String exceptionMessage = constraintViolationException.getMessage();
StringBuilder messages = new StringBuilder();
if (exceptionMessage.indexOf(StrUtil.COMMA) > 0) {
@@ -161,7 +168,7 @@ public class GlobalBusinessExceptionHandler {
}
// 然后提取错误提示信息进行返回
LogUtil.njcnDebug(log, "参数校验异常,异常为:{}", messages);
// logService.recodeBusinessExceptionLog(constraintViolationException, httpServletRequest, CommonResponseEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION.getMessage());
recodeAdviceLog(constraintViolationException, CommonResponseEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION.getMessage());
List<ConstraintViolation<?>> constraintViolationList = new ArrayList<>(constraintViolationException.getConstraintViolations());
ConstraintViolation<?> constraintViolation = constraintViolationList.get(0);
Class<?> rootBeanClass = constraintViolation.getRootBeanClass();
@@ -182,24 +189,12 @@ public class GlobalBusinessExceptionHandler {
* @param illegalArgumentException 参数校验异常
*/
@ExceptionHandler(IllegalArgumentException.class)
public HttpResult<String> handleIndexOutOfBoundsException(HttpServletRequest httpServletRequest, IllegalArgumentException illegalArgumentException) {
public HttpResult<String> handleIndexOutOfBoundsException(IllegalArgumentException illegalArgumentException) {
LogUtil.logExceptionStackInfo(CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION.getMessage(), illegalArgumentException);
// logService.recodeBusinessExceptionLog(illegalArgumentException, httpServletRequest, CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION.getMessage());
recodeAdviceLog(illegalArgumentException, CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION.getMessage());
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION, illegalArgumentException.getMessage(), ReflectCommonUtil.getMethodDescribeByException(illegalArgumentException));
}
// /**
// * 表格校验异常
// *
// * @param excelAnalysisException 表格校验异常
// */
// @ExceptionHandler(ExcelAnalysisException.class)
// public HttpResult<String> handleExcelAnalysisException(HttpServletRequest httpServletRequest, ExcelAnalysisException excelAnalysisException) {
// LogUtil.logExceptionStackInfo(CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION.getMessage(), excelAnalysisException);
// // logService.recodeBusinessExceptionLog(excelAnalysisException, httpServletRequest,CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION.getMessage());
// return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION, excelAnalysisException.getCause().getMessage(), ReflectCommonUtil.getMethodDescribeByException(excelAnalysisException));
// }
/**
* 未声明异常捕捉
@@ -207,7 +202,7 @@ public class GlobalBusinessExceptionHandler {
* @param exception 未声明异常
*/
@ExceptionHandler(Exception.class)
public HttpResult<String> handleException(HttpServletRequest httpServletRequest, Exception exception) {
public HttpResult<String> handleException(Exception exception) {
//针对fallbackFactory降级异常特殊处理
Exception tempException = exception;
String exceptionCause = CommonResponseEnum.UN_DECLARE.getMessage();
@@ -224,7 +219,7 @@ public class GlobalBusinessExceptionHandler {
}
}
LogUtil.logExceptionStackInfo(exceptionCause, tempException);
// logService.recodeBusinessExceptionLog(tempException, httpServletRequest, exceptionCause);
recodeAdviceLog(exception, exceptionCause);
//判断方法上是否有自定义注解,做特殊处理
// Method method = ReflectCommonUtil.getMethod(exception);
// if (!Objects.isNull(method)){
@@ -236,18 +231,30 @@ public class GlobalBusinessExceptionHandler {
}
/**
* json解析异常
*
* @param jsonException json参数
*/
@ExceptionHandler(JSONException.class)
public HttpResult<String> handleIndexOutOfBoundsException(HttpServletRequest httpServletRequest, JSONException jsonException) {
public HttpResult<String> handleIndexOutOfBoundsException(JSONException jsonException) {
LogUtil.logExceptionStackInfo(CommonResponseEnum.JSON_CONVERT_EXCEPTION.getMessage(), jsonException);
// logService.recodeBusinessExceptionLog(jsonException, httpServletRequest, CommonResponseEnum.JSON_CONVERT_EXCEPTION.getMessage());
recodeAdviceLog(jsonException, CommonResponseEnum.JSON_CONVERT_EXCEPTION.getMessage());
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.JSON_CONVERT_EXCEPTION, jsonException.getMessage(), ReflectCommonUtil.getMethodDescribeByException(jsonException));
}
private void recodeAdviceLog(Exception businessException, String methodDescribe) {
HttpServletRequest httpServletRequest = HttpServletUtil.getRequest();
Future<?> future = executor.submit(() -> {
HttpServletUtil.setRequest(httpServletRequest);
sysLogAuditService.recodeBusinessExceptionLog(businessException, methodDescribe);
});
try {
// 抛出 ExecutionException
future.get();
} catch (ExecutionException | InterruptedException e) {
log.error("保存审计日志异常,异常为:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,37 @@
package com.njcn;
import com.njcn.gather.EntranceApplication;
import com.njcn.gather.report.pojo.DevReportParam;
import com.njcn.gather.report.service.IPqReportService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
/**
* @author hongawen
* @version 1.0.0
* @date 2021年12月10日 15:05
*/
@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = EntranceApplication.class)
public class BaseJunitTest {
@Autowired
private IPqReportService pqReportService;
@Test
public void test() {
DevReportParam devReportParam = new DevReportParam();
devReportParam.setPlanId("ad3df9e4a90b4c3c8ce7d21a84ce6f59");
devReportParam.setPlanCode("31");
devReportParam.setScriptId("810e4050e1d445e3542c998a077a263a");
devReportParam.setDevId("a46349a3b3cf4789a6b82690a6076afd");
pqReportService.generateReport(devReportParam);
}
}

View File

@@ -1,7 +1,9 @@
package com.njcn.gather.storage.pojo.param;
import io.swagger.models.auth.In;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
@@ -12,6 +14,8 @@ import java.util.List;
* @data 2025/1/10 16:06
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SingleNonHarmParam implements Serializable {
private static final long serialVersionUID = 1L;
@@ -36,6 +40,11 @@ public class SingleNonHarmParam implements Serializable {
*/
private String adType;
/**
* 测试项类型,暂态的存在多个
*/
private List<String> valueTypeList;
/**
* 测试项所在脚本的位置
*/
@@ -43,4 +52,35 @@ public class SingleNonHarmParam implements Serializable {
private List<Integer> sortList;
public SingleNonHarmParam(Integer planCode, String devId, Integer channelNo, String adType, Integer sort) {
this.planCode = planCode;
this.devId = devId;
this.channelNo = channelNo;
this.adType = adType;
this.sort = sort;
}
public SingleNonHarmParam(Integer planCode, String devId, Integer channelNo, String adType, List<Integer> sortList) {
this.planCode = planCode;
this.devId = devId;
this.channelNo = channelNo;
this.adType = adType;
this.sortList = sortList;
}
public SingleNonHarmParam(Integer planCode, String devId, Integer channelNo, List<String> valueTypeList, Integer sort) {
this.planCode = planCode;
this.devId = devId;
this.channelNo = channelNo;
this.valueTypeList = valueTypeList;
this.sort = sort;
}
public SingleNonHarmParam(Integer planCode, String devId, Integer channelNo, List<String> valueTypeList, List<Integer> sortList) {
this.planCode = planCode;
this.devId = devId;
this.channelNo = channelNo;
this.valueTypeList = valueTypeList;
this.sortList = sortList;
}
}

View File

@@ -55,4 +55,12 @@ public interface AdNonHarmonicService extends IService<AdNonHarmonicResult> {
* @return
*/
List<AdNonHarmonicResult> listAll(String scriptId,String code,String devId);
/**
* 根据查询条件查找结果
* @param param 查询条件
*/
List<AdNonHarmonicResult> queryByCondition(SingleNonHarmParam param);
}

View File

@@ -230,6 +230,7 @@ public class AdHarmonicServiceImpl extends ServiceImpl<AdHarmonicMappper, AdHarm
.eq(AdHarmonicResult::getSort, singleNonHarmParam.getSort())
.eq(AdHarmonicResult::getAdType, singleNonHarmParam.getAdType());
List<AdHarmonicResult> adHarmonicResults = this.getBaseMapper().selectJoinList(AdHarmonicResult.class, wrapper);
DynamicTableNameHandler.remove();
if (CollectionUtil.isNotEmpty(adHarmonicResults)) {
return adHarmonicResults.get(0);
}

View File

@@ -14,6 +14,7 @@ import com.njcn.gather.storage.mapper.AdNonHarmonicMapper;
import com.njcn.gather.storage.pojo.param.SingleNonHarmParam;
import com.njcn.gather.storage.pojo.param.StorageParam;
import com.njcn.gather.storage.pojo.po.AdBaseResult;
import com.njcn.gather.storage.pojo.po.AdHarmonicResult;
import com.njcn.gather.storage.pojo.po.AdNonHarmonicResult;
import com.njcn.gather.storage.pojo.vo.RawDataVO;
import com.njcn.gather.storage.pojo.vo.RawResultDataVO;
@@ -164,6 +165,20 @@ public class AdNonHarmonicServiceImpl extends ServiceImpl<AdNonHarmonicMapper, A
return results;
}
@Override
public List<AdNonHarmonicResult> queryByCondition(SingleNonHarmParam param) {
String prefix = "ad_non_harmonic_result_";
DynamicTableNameHandler.setTableName(prefix + param.getPlanCode());
MPJLambdaWrapper<AdNonHarmonicResult> wrapper = new MPJLambdaWrapper<>();
wrapper.like(AdNonHarmonicResult::getMonitorId, param.getDevId() + "_" + param.getChannelNo())
.in(AdNonHarmonicResult::getSort, param.getSortList())
.in(AdNonHarmonicResult::getAdType, param.getValueTypeList());
List<AdNonHarmonicResult> adNonHarmonicResults = this.getBaseMapper().selectJoinList(AdNonHarmonicResult.class, wrapper);
DynamicTableNameHandler.remove();
return adNonHarmonicResults;
}
private String unit(String code) {
String unit = "";
switch (code) {

View File

@@ -1,153 +0,0 @@
package com.njcn.gather.system.log.aop;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.annotation.OperateInfo;
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.db.mybatisplus.constant.UserConstant;
import com.njcn.gather.system.log.pojo.enums.LogLevelEnum;
import com.njcn.gather.system.log.pojo.enums.LogOperationTypeEnum;
import com.njcn.gather.system.log.pojo.po.SysLogAudit;
import com.njcn.gather.system.log.service.ISysLogAuditService;
import com.njcn.gather.system.pojo.enums.SystemResponseEnum;
import com.njcn.gather.user.user.pojo.param.SysUserParam;
import com.njcn.gather.user.user.pojo.po.SysUser;
import com.njcn.gather.user.user.service.ISysUserService;
import com.njcn.web.utils.RequestUtil;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
/**
* @author caozehui
* @data 2024-12-2
*/
@Aspect
@Component
@RequiredArgsConstructor
public class LogAdvice implements ApplicationListener<ContextRefreshedEvent> {
private final ISysLogAuditService sysLogAuditService;
private final ISysUserService sysUserService;
private final List<String> IGNORE_METHOD = new ArrayList<>(Arrays.asList("login"));
private BlockingQueue<SysLogAudit> logQueue = new LinkedBlockingDeque<>();
// @Pointcut(value = "execution(* com.njcn.gather..controller.*.*(..)) && !execution(* com.njcn.gather..controller.AuthController.*(..))")
@Pointcut(value = "execution(* com.njcn.gather..controller.*.*(..))")
public void logPointcut() {
}
@Around("logPointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String name = joinPoint.getSignature().getName();
String username = "";
// 因为刚开始登录时无法获取到token针对login方法单独处理
if (IGNORE_METHOD.contains(name)) {
Object[] args = joinPoint.getArgs();
Object arg = args[0];
if (arg instanceof SysUserParam.LoginParam) {
username = ((SysUserParam.LoginParam) arg).getUsername();
}
} else {
String userId = RequestUtil.getUserId();
if (StrUtil.isNotBlank(userId)) {
SysUser user = sysUserService.getById(RequestUtil.getUserId());
if (ObjectUtil.isNotNull(user)) {
username = user.getName();
}
}
}
Object result = null;
try {
sysLogAuditService.scheduleRemoveLog();
result = joinPoint.proceed();
if (result instanceof HttpResult) {
HttpResult result1 = (HttpResult) result;
if (CommonResponseEnum.SUCCESS.getCode().equals(result1.getCode())) {
addLogToQueue(joinPoint, "".equals(username) ? UserConstant.UNKNOWN_USER_ID : username, LogOperationTypeEnum.OPERATE.getMsg(), 1, LogLevelEnum.INFO.getCode(), 0);
} else {
addLogToQueue(joinPoint, "".equals(username) ? UserConstant.UNKNOWN_USER_ID : username, LogOperationTypeEnum.OPERATE.getMsg(), 0, LogLevelEnum.INFO.getCode(), 0);
}
} else {
addLogToQueue(joinPoint, "".equals(username) ? UserConstant.UNKNOWN_USER_ID : username, LogOperationTypeEnum.OPERATE.getMsg(), 1, LogLevelEnum.INFO.getCode(), 0);
}
} catch (Throwable e) {
addLogToQueue(joinPoint, "".equals(username) ? UserConstant.UNKNOWN_USER_ID : username, LogOperationTypeEnum.OPERATE.getMsg(), 0, LogLevelEnum.ERROR.getCode(), 1);
throw e;
}
return result;
}
private void addLogToQueue(ProceedingJoinPoint joinPoint, String username, String operationType, Integer result, Integer level, Integer warn) {
SysLogAudit sysLogAudit = new SysLogAudit();
sysLogAudit.setOperateType(operationType);
sysLogAudit.setLevel(level);
sysLogAudit.setWarn(warn); //0-未告警1-告警
sysLogAudit.setCreateBy(username);
sysLogAudit.setIp(RequestUtil.getUserIp());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
OperateInfo operateInfo = method.getAnnotation(OperateInfo.class);
ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
String resultStr = (result == 1 ? "成功" : "失败");
if (operateInfo != null) {
//注解上的操作类型
sysLogAudit.setResult(operateInfo.operateType() + resultStr);
}
if (apiOperation != null) {
//注解上的描述
String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
sysLogAudit.setRemark(username + ":" + now + " " + apiOperation.value() + " " + resultStr);
}
//Object[] args = joinPoint.getArgs();
logQueue.add(sysLogAudit);
}
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
List<SysLogAudit> logList = new ArrayList<>();
SysLogAudit log;
while ((log = logQueue.poll(5, TimeUnit.MILLISECONDS)) != null) {
log.setSort(sysLogAuditService.getMaxSort() + 1);
logList.add(log);
}
if (!logList.isEmpty()) {
sysLogAuditService.saveBatch(logList); // 假设有一个批量保存的方法
}
} catch (Exception e) {
throw new BusinessException(SystemResponseEnum.LOG_RECORD_FAILED);
}
}
}
}).start();
}
}

View File

@@ -28,9 +28,16 @@ public class SysLogAudit implements Serializable {
private String id;
/**
* 操作类型
* 登录名
*/
private String operateType;
private String loginName;
/**
* 用户名
*/
private String userName;
/**
* IP
@@ -38,14 +45,31 @@ public class SysLogAudit implements Serializable {
private String ip;
/**
* 事件结果
* 操作内容
*/
private String operate;
/**
* 操作类型 (比如:查询、新增、删除、下载等等)
*/
private String operateType;
/**
* 事件结果 0失败 1成功
*/
private String result;
/**
* 事件描述
* 失败原因
*/
private String remark;
private String reason;
/**
* 事件类型
*/
private Integer type;
/**
* 事件严重度(0.普通 1.中等 2.严重)
@@ -57,25 +81,14 @@ public class SysLogAudit implements Serializable {
*/
private Integer warn;
/**
* 创建用户
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 排序
*/
private Integer sort;
/**
* 创建时间
* 日志发生事件
*/
@TableField(fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createTime;
private LocalDateTime logTime;
}

View File

@@ -2,8 +2,15 @@ package com.njcn.gather.system.log.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.gather.system.log.pojo.param.SysLogParam;
import com.njcn.gather.system.log.pojo.po.SysLogAudit;
import com.njcn.gather.system.log.pojo.vo.SysLogVO;
import io.swagger.models.auth.In;
import org.aspectj.lang.JoinPoint;
import org.springframework.core.MethodParameter;
import org.springframework.http.server.ServerHttpRequest;
/**
* @author caozehui
@@ -26,22 +33,20 @@ public interface ISysLogAuditService extends IService<SysLogAudit> {
*/
void exportCSV(SysLogParam.QueryParam param);
/**
* 获取最大的排序值
*
* @return
*/
Integer getMaxSort();
/**
* 按照 一定规则 清除日志
*/
void scheduleRemoveLog();
/**
* 分析日志
*
* @param param
*/
void analyse(SysLogParam.QueryParam param);
void recodeAdviceLog(MethodParameter returnType, HttpResult<?> httpResult, String methodDescribe);
/**
* 全局异常拦截器的捕获的异常进行日志记录入库
*
* @param businessException 异常
* @param message 异常描述
*/
void recodeBusinessExceptionLog(Exception businessException, String message);
}

View File

@@ -6,31 +6,41 @@ import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.constant.LogInfo;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.PubUtils;
import com.njcn.db.mybatisplus.constant.UserConstant;
import com.njcn.gather.system.log.mapper.SysLogAuditMapper;
import com.njcn.gather.system.log.pojo.enums.LogLevelEnum;
import com.njcn.gather.system.log.pojo.param.SysLogParam;
import com.njcn.gather.system.log.pojo.po.SysLogAudit;
import com.njcn.gather.system.log.service.ISysLogAuditService;
import com.njcn.gather.system.log.util.CSVUtil;
import com.njcn.gather.user.user.pojo.po.SysUser;
import com.njcn.gather.user.user.service.ISysUserService;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.utils.HttpServletUtil;
import com.njcn.web.utils.ReflectCommonUtil;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer;
import org.springframework.core.MethodParameter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.MethodArgumentNotValidException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -42,6 +52,8 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class SysLogAuditServiceImpl extends ServiceImpl<SysLogAuditMapper, SysLogAudit> implements ISysLogAuditService {
private final ISysUserService sysUserService;
@Override
public Page<SysLogAudit> listSysLogAudit(SysLogParam.QueryParam param) {
QueryWrapper<SysLogAudit> queryWrapper = new QueryWrapper<>();
@@ -57,55 +69,41 @@ public class SysLogAuditServiceImpl extends ServiceImpl<SysLogAuditMapper, SysLo
@Override
public void exportCSV(SysLogParam.QueryParam param) {
String[] titles = {"日志类型", "IP", "事件结果", "描述", "日志等级", "告警标准", "操作用户", "记录时间"};
String[] keys = {"operateType", "ip", "result", "remark", "level", "warn", "createBy", "createTime"};
QueryWrapper<SysLogAudit> queryWrapper = new QueryWrapper<>();
if (ObjectUtil.isNotNull(param)) {
queryWrapper
.eq(StrUtil.isNotBlank(param.getOperateType()), "sys_log_audit.Operate_Type", param.getOperateType())
.eq(StrUtil.isNotBlank(param.getCreateBy()), "sys_log_audit.Create_By", param.getCreateBy())
.between(StrUtil.isAllNotBlank(param.getSearchBeginTime(), param.getSearchEndTime()), "sys_log_audit.Create_Time", param.getSearchBeginTime(), param.getSearchEndTime());
}
queryWrapper.orderByDesc("sys_log_audit.Create_Time");
List<SysLogAudit> list = this.list(queryWrapper);
List<Map<String, Object>> data = list.stream().map(item -> {
Map<String, Object> map = new HashMap<>();
map.put("operateType", item.getOperateType());
map.put("ip", item.getIp());
map.put("result", item.getResult());
map.put("remark", item.getRemark());
map.put("level", LogLevelEnum.getEnum(item.getLevel()).getMsg());
map.put("warn", item.getWarn() == 0 ? "未告警" : "告警");
map.put("createBy", item.getCreateBy());
//将 createTime 转换为 yyyy-MM-dd HH:mm:ss 格式
map.put("createTime", item.getCreateTime() == null ? "" : item.getCreateTime().toString().replace("T", " "));
return map;
}).collect(Collectors.toList());
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
CSVUtil.export("日志数据" + sdf.format(new Date()) + ".csv", titles, keys, data);
}
@Override
public Integer getMaxSort() {
SysLogAudit log = this.lambdaQuery().orderByDesc(SysLogAudit::getSort).last("limit 1").one();
if (ObjectUtil.isNotNull(log)) {
return log.getSort();
}
return 0;
}
@Override
public void scheduleRemoveLog() {
QueryWrapper<SysLogAudit> wrapper = new QueryWrapper<>();
wrapper.lt("Create_Time", LocalDateTime.now().minusDays(30 * 6));
int count = this.count();
if (count > 1e6) {
wrapper.or().orderByAsc("Create_Time").last("limit " + (count - 1e6));
}
this.remove(wrapper);
// String[] titles = {"日志类型", "IP", "事件结果", "描述", "日志等级", "告警标准", "操作用户", "记录时间"};
// String[] keys = {"operateType", "ip", "result", "remark", "level", "warn", "createBy", "createTime"};
// QueryWrapper<SysLogAudit> queryWrapper = new QueryWrapper<>();
// if (ObjectUtil.isNotNull(param)) {
// queryWrapper
// .eq(StrUtil.isNotBlank(param.getOperateType()), "sys_log_audit.Operate_Type", param.getOperateType())
// .eq(StrUtil.isNotBlank(param.getCreateBy()), "sys_log_audit.Create_By", param.getCreateBy())
// .between(StrUtil.isAllNotBlank(param.getSearchBeginTime(), param.getSearchEndTime()), "sys_log_audit.Create_Time", param.getSearchBeginTime(), param.getSearchEndTime());
// }
// queryWrapper.orderByDesc("sys_log_audit.Create_Time");
// List<SysLogAudit> list = this.list(queryWrapper);
// List<Map<String, Object>> data = list.stream().map(item -> {
// Map<String, Object> map = new HashMap<>();
// map.put("operateType", item.getOperateType());
// map.put("ip", item.getIp());
// map.put("result", item.getResult());
// map.put("remark", item.getRemark());
// map.put("level", LogLevelEnum.getEnum(item.getLevel()).getMsg());
// map.put("warn", item.getWarn() == 0 ? "未告警" : "告警");
// map.put("createBy", item.getCreateBy());
// //将 createTime 转换为 yyyy-MM-dd HH:mm:ss 格式
// map.put("createTime", item.getCreateTime() == null ? "" : item.getCreateTime().toString().replace("T", " "));
// return map;
// }).collect(Collectors.toList());
// SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
// CSVUtil.export("日志数据" + sdf.format(new Date()) + ".csv", titles, keys, data);
}
/**
* 暂时废弃,有需要再重新写
*
* @param param .
*/
@Override
@Deprecated
public void analyse(SysLogParam.QueryParam param) {
QueryWrapper<SysLogAudit> queryWrapper = new QueryWrapper<>();
if (ObjectUtil.isNotNull(param)) {
@@ -114,11 +112,113 @@ public class SysLogAuditServiceImpl extends ServiceImpl<SysLogAuditMapper, SysLo
.between(StrUtil.isAllNotBlank(param.getSearchBeginTime(), param.getSearchEndTime()), "sys_log_audit.Create_Time", param.getSearchBeginTime(), param.getSearchEndTime());
}
List<SysLogAudit> list = this.list(queryWrapper);
Map<String, Long> collect = list.stream().collect(Collectors.groupingBy(SysLogAudit::getCreateBy, Collectors.counting()));
Map<String, Long> collect = list.stream().collect(Collectors.groupingBy(SysLogAudit::getLoginName, Collectors.counting()));
this.exportAnalyseExcel("分析结果", collect);
}
/**
* 记录controller全局拦截的日志并非全局异常捕获的
*
* @param returnType 返回类型
* @param httpResult 返回结果
* @param methodDescribe 方法描述
*/
@Override
public void recodeAdviceLog(MethodParameter returnType, HttpResult<?> httpResult, String methodDescribe) {
SysLogAudit sysLogAudit = new SysLogAudit();
String userId = RequestUtil.getUserId();
if (StrUtil.isBlank(userId)) {
String loginName = RequestUtil.getLoginName();
if (loginName.equalsIgnoreCase(LogInfo.UNKNOWN_USER)) {
sysLogAudit.setUserName(UserConstant.UN_LOGIN);
sysLogAudit.setLoginName(UserConstant.UN_LOGIN);
} else {
sysLogAudit.setUserName(loginName);
sysLogAudit.setLoginName(loginName);
}
} else {
SysUser sysUser = sysUserService.getById(userId);
if (sysUser != null) {
sysLogAudit.setUserName(sysUser.getName());
sysLogAudit.setLoginName(sysUser.getLoginName());
} else {
sysLogAudit.setUserName(UserConstant.UN_LOGIN);
sysLogAudit.setLoginName(UserConstant.UN_LOGIN);
}
}
// 不是很重要,本地部署的除非以后工具类C端有必要到时候再丰富读IP的过程
sysLogAudit.setIp(RequestUtil.getUserIp());
// 操作内容
sysLogAudit.setOperate(methodDescribe);
// 操作结果
String result = httpResult.getCode().equalsIgnoreCase(CommonResponseEnum.FAIL.getCode()) ? CommonResponseEnum.FAIL.getMessage() : CommonResponseEnum.SUCCESS.getMessage();
sysLogAudit.setResult(result);
// 事件类型
String type = ReflectCommonUtil.getOperateInfoByMethod(returnType.getMethod()).getOperateType();
sysLogAudit.setType(type.equalsIgnoreCase("业务事件") ? 0 : 1);
// 等级
String level = ReflectCommonUtil.getOperateInfoByMethod(returnType.getMethod()).getOperateLevel();
sysLogAudit.setLevel(levelStringToNumber(level));
// 中等以上的需要置为未告警,但是告警功能暂未做
if(sysLogAudit.getLevel() == 0){
sysLogAudit.setWarn(1);
}else{
sysLogAudit.setWarn(0);
}
// 操作类型
String operateType = ReflectCommonUtil.getOperateTypeByMethod(returnType.getMethod());
sysLogAudit.setOperateType(operateType);
sysLogAudit.setLogTime(LocalDateTime.now());
this.save(sysLogAudit);
}
@Override
public void recodeBusinessExceptionLog(Exception exception, String message) {
SysLogAudit sysLogAudit = new SysLogAudit();
String userId = RequestUtil.getUserId();
if (StrUtil.isBlank(userId)) {
String loginName = RequestUtil.getLoginName();
if (loginName.equalsIgnoreCase(LogInfo.UNKNOWN_USER)) {
sysLogAudit.setUserName(UserConstant.UN_LOGIN);
sysLogAudit.setLoginName(UserConstant.UN_LOGIN);
} else {
sysLogAudit.setUserName(loginName);
sysLogAudit.setLoginName(loginName);
}
} else {
SysUser sysUser = sysUserService.getById(userId);
if (sysUser != null) {
sysLogAudit.setUserName(sysUser.getName());
sysLogAudit.setLoginName(sysUser.getLoginName());
} else {
sysLogAudit.setUserName(UserConstant.UN_LOGIN);
sysLogAudit.setLoginName(UserConstant.UN_LOGIN);
}
}
//根据异常获取method方法
Method method = ReflectCommonUtil.getMethod(exception);
if (exception instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException) exception;
method = methodArgumentNotValidException.getParameter().getMethod();
}
// 不是很重要,本地部署的除非以后工具类C端有必要到时候再丰富读IP的过程
sysLogAudit.setIp(RequestUtil.getUserIp());
sysLogAudit.setReason(message);
String result = CommonResponseEnum.FAIL.getMessage();
sysLogAudit.setResult(result);
String methodDescribe = ReflectCommonUtil.getMethodDescribeByMethod(method);
sysLogAudit.setOperate(methodDescribe);
String type = ReflectCommonUtil.getOperateInfoByMethod(method).getOperateType();
sysLogAudit.setType(type.equalsIgnoreCase("业务事件") ? 0 : 1);
String level = ReflectCommonUtil.getOperateInfoByMethod(method).getOperateLevel();
sysLogAudit.setLevel(levelStringToNumber(level));
String operateType = ReflectCommonUtil.getOperateTypeByMethod(method);
sysLogAudit.setOperateType(operateType);
sysLogAudit.setLogTime(LocalDateTime.now());
this.save(sysLogAudit);
}
private void exportAnalyseExcel(String fileName, Map<String, Long> collect) {
HttpServletResponse response = HttpServletUtil.getResponse();
XSSFWorkbook wb = new XSSFWorkbook();
@@ -140,95 +240,19 @@ public class SysLogAuditServiceImpl extends ServiceImpl<SysLogAuditMapper, SysLo
}
}
// /**
// * 创建饼图
// *
// * @param sheet
// * @param categoryList
// * @param sheetDataList
// */
// private void createPieChart(XSSFSheet sheet, List<String> categoryList, List<Long> sheetDataList) {
// int row1 = 0;
// int row2 = 8;
// int col1 = 0;
// int col2 = 30;
// // 设置图表列宽
// for (int i = 0; i < row2; i++) {
// sheet.setColumnWidth(i, 6000);
// }
// //y轴显示数据
// String[] headArray = categoryList.stream().collect(Collectors.toList()).toArray(new String[]{});
//
// // Create a chart
// XSSFDrawing drawing = sheet.createDrawingPatriarch();
// ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, col1, row1, row2, col2);
// XSSFChart chart = drawing.createChart(anchor);
// //标题是否覆盖图表
// chart.setTitleOverlay(false);
// //设置图表标题
// chart.setTitleText("分析结果");
// // 创建图表系列
// XDDFChartLegend legend = chart.getOrAddLegend();
// legend.setPosition(LegendPosition.TOP);
//
// //动态数据
// //x轴数据
// XDDFDataSource<String> xData = XDDFDataSourcesFactory.fromArray(headArray);
// //饼图数据
// XDDFPieChartData data = (XDDFPieChartData) chart.createData(ChartTypes.PIE, null, null);
// data.setVaryColors(true);
//
// Double[] yArray = sheetDataList.stream().collect(Collectors.toList()).toArray(new Double[]{});
// //y轴数据
// XDDFNumericalDataSource<Double> yData = XDDFDataSourcesFactory.fromArray(yArray);
// XDDFChartData.Series series = data.addSeries(xData, yData);
//
// //series.setTitle("title", null);
// series.setShowLeaderLines(true);
// // 隐藏图例标识、系列名称、分类名称和数值
// XDDFPieChartData.Series s = (XDDFPieChartData.Series) series;
// CTPieSer ctPieSer = s.getCTPieSer();
// showCateName(ctPieSer, false);
// showVal(ctPieSer, false);
// showLegendKey(ctPieSer, false);
// showSerName(ctPieSer, false);
//
// chart.plot(data);
// }
// 控制值系列名称是否显示
private void showSerName(CTPieSer series, boolean val) {
if (series.getDLbls().isSetShowSerName()) {
series.getDLbls().getShowSerName().setVal(val);
} else {
series.getDLbls().addNewShowSerName().setVal(val);
}
}
// 控制分类名称是否显示
private void showCateName(CTPieSer series, boolean val) {
if (series.getDLbls().isSetShowCatName()) {
series.getDLbls().getShowCatName().setVal(val);
} else {
series.getDLbls().addNewShowCatName().setVal(val);
}
}
// 控制值是否显示
private void showVal(CTPieSer series, boolean val) {
if (series.getDLbls().isSetShowVal()) {
series.getDLbls().getShowVal().setVal(val);
} else {
series.getDLbls().addNewShowVal().setVal(val);
}
}
// 控制图例标识是否显示
private void showLegendKey(CTPieSer series, boolean val) {
if (series.getDLbls().isSetShowLegendKey()) {
series.getDLbls().getShowLegendKey().setVal(val);
} else {
series.getDLbls().addNewShowLegendKey().setVal(val);
/**
* 严重度 文字 转 数字
*/
private Integer levelStringToNumber(String level) {
switch (level) {
case "中等":
return 1;
case "严重":
return 2;
default:
return 0;
}
}
}

View File

@@ -37,6 +37,7 @@
<version>1.2.83</version>
</dependency>
</dependencies>
</project>

View File

@@ -30,6 +30,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.security.KeyPair;
import java.util.Base64;
import java.util.HashMap;
@@ -53,7 +54,7 @@ public class AuthController extends BaseController {
@OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.AUTHENTICATE)
@PostMapping("/login")
@ApiOperation("登录")
public HttpResult<Object> login(@RequestBody SysUserParam.LoginParam param) {
public HttpResult<Object> login(@RequestBody SysUserParam.LoginParam param, HttpServletRequest request) {
String methodDescribe = getMethodDescribe("login");
LogUtil.njcnDebug(log, "{},登录参数为:{}", methodDescribe, param);
byte[] decode = Base64.getDecoder().decode(param.getUsername());
@@ -73,7 +74,8 @@ public class AuthController extends BaseController {
} catch (Exception e) {
throw new BusinessException(UserResponseEnum.RSA_DECRYT_ERROR);
}
// 因不确定是否能登陆成功先将登陆名保存到request一遍记录谁执行了登录操作
request.setAttribute(SecurityConstants.AUTHENTICATE_USERNAME, username);
SysUser user = sysUserService.getUserByLoginNameAndPassword(username, password);
if (ObjectUtil.isNull(user)) {
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, UserValidMessage.LOGIN_FAILED);
@@ -112,9 +114,9 @@ public class AuthController extends BaseController {
}
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
@ApiOperation("刷新token")
@ApiOperation("刷新accessToken")
@GetMapping("/refreshToken")
public HttpResult<Object> refreshToken() {
public HttpResult<Object> refreshToken(HttpServletRequest request) {
String methodDescribe = getMethodDescribe("refreshToken");
LogUtil.njcnDebug(log, "{}刷新token", methodDescribe);
String accessToken = RequestUtil.getAccessToken();
@@ -125,7 +127,7 @@ public class AuthController extends BaseController {
String userId = (String) map.get(SecurityConstants.USER_ID);
SysUser user = sysUserService.getById(userId);
String accessTokenNew = JwtUtil.getAccessToken(userId);
request.setAttribute(SecurityConstants.AUTHENTICATE_USERNAME, user.getLoginName());
//String refreshTokenNew = JwtUtil.getRefreshToken(accessTokenNew);
token.setAccessToken(accessTokenNew);
@@ -141,9 +143,11 @@ public class AuthController extends BaseController {
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
@ApiOperation("获取RSA公钥")
@GetMapping("/getPublicKey")
public HttpResult<String> publicKey(@RequestParam("username") String username, @RequestParam("checked") Boolean checked) throws Exception {
public HttpResult<String> publicKey(@RequestParam("username") String username, @RequestParam("checked") Boolean checked, HttpServletRequest request) throws Exception {
String methodDescribe = getMethodDescribe("publicKey");
LogUtil.njcnDebug(log, "{}获取RSA公钥", methodDescribe);
// 因不确定是否能登陆成功先将登陆名保存到request一遍记录谁执行了登录操作
request.setAttribute(SecurityConstants.AUTHENTICATE_USERNAME, username);
keyPair = RSAUtil.generateKeyPair();
if (checked) {
Map map = new HashMap();