diff --git a/pom.xml b/pom.xml index 6edfd6972..b5e1ee387 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,12 @@ + + org.sejda.imageio + webp-imageio + 0.1.6 + compile + org.jetbrains annotations diff --git a/src/main/java/com/zhgd/xmgl/modules/basicdata/controller/UploadFileController.java b/src/main/java/com/zhgd/xmgl/modules/basicdata/controller/UploadFileController.java index 90883b59c..eb37f76b4 100644 --- a/src/main/java/com/zhgd/xmgl/modules/basicdata/controller/UploadFileController.java +++ b/src/main/java/com/zhgd/xmgl/modules/basicdata/controller/UploadFileController.java @@ -1,6 +1,9 @@ package com.zhgd.xmgl.modules.basicdata.controller; +import com.luciad.imageio.webp.WebPWriteParam; +import com.zhgd.annotation.OperLog; +import com.zhgd.jeecg.common.api.vo.Result; import com.zhgd.jeecg.common.execption.OpenAlertException; import com.zhgd.xmgl.modules.basicdata.service.UploadFileService; import io.swagger.annotations.Api; @@ -12,12 +15,19 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; import javax.servlet.http.HttpServletResponse; -import java.io.InputStream; -import java.io.OutputStream; +import java.awt.image.BufferedImage; +import java.io.*; import java.net.URL; import java.net.URLConnection; +import java.util.Base64; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; /** @@ -115,4 +125,96 @@ public class UploadFileController { log.error("err:", e); } } + + @OperLog(operModul = "转换图片成webp格式", operType = "", operDesc = "转换图片成webp格式") + @PostMapping("/convertBase642webp") + public Result convertBase642webp(@RequestBody Map request, + HttpServletResponse response) { + String convertAndBase64Webp = null; + try { + // 1. 获取并验证Base64数据 + String base64Data = request.get("base64"); + if (base64Data == null || base64Data.isEmpty()) { + throw new OpenAlertException("Base64 data is required"); + } + + // 2. 清理Data URL前缀 + String pureBase64 = base64Data; + if (base64Data.contains(",")) { + pureBase64 = base64Data.split(",")[1]; + } + + // 3. 设置响应头,触发文件下载 + setDownloadHeaders(response, "converted_image.webp"); + + // 4. 获取输出流并直接写入WEBP数据 + convertAndBase64Webp = getConvertAndBase64Webp(pureBase64); + } catch (Exception e) { + throw new OpenAlertException("Conversion failed: " + e.getMessage(), e); + } + return Result.success(convertAndBase64Webp); + } + + /** + * 设置文件下载的HTTP头 + */ + private void setDownloadHeaders(HttpServletResponse response, String filename) { + response.setContentType("image/webp"); + response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\""); + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); + } + + /** + * 转换Base64图像为WebP格式并返回Base64字符串 + */ + private String getConvertAndBase64Webp(String pureBase64) throws IOException { + // 解码Base64 + byte[] imageBytes = Base64.getDecoder().decode(pureBase64); + + // 读取原始图片 + BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes)); + if (image == null) { + throw new OpenAlertException("Invalid image data"); + } + + // 获取WebP ImageWriter + Iterator writers = ImageIO.getImageWritersByMIMEType("image/webp"); + if (!writers.hasNext()) { + throw new OpenAlertException("No WebP ImageWriter found"); + } + + ImageWriter writer = writers.next(); + + // 使用ByteArrayOutputStream来捕获WebP图像数据 + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(baos)) { + + if (imageOutputStream == null) { + throw new OpenAlertException("Cannot create ImageOutputStream"); + } + + writer.setOutput(imageOutputStream); + + // 配置WebP写入参数 + WebPWriteParam writeParam = new WebPWriteParam(writer.getLocale()); + writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + writeParam.setCompressionType(writeParam.getCompressionTypes()[WebPWriteParam.LOSSY_COMPRESSION]); + writeParam.setCompressionQuality(0.8f); + + // 写入图像 + writer.write(null, new IIOImage(image, null, null), writeParam); + + // 确保所有数据都写入输出流 + imageOutputStream.flush(); + + // 获取WebP图像的字节数组并转换为Base64 + byte[] webpBytes = baos.toByteArray(); + return Base64.getEncoder().encodeToString(webpBytes); + + } finally { + writer.dispose(); + } + } } diff --git a/src/main/java/com/zhgd/xmgl/modules/broadcast/controller/SmartBroadcastDevController.java b/src/main/java/com/zhgd/xmgl/modules/broadcast/controller/SmartBroadcastDevController.java index 1763cb885..d0b9fa9d6 100644 --- a/src/main/java/com/zhgd/xmgl/modules/broadcast/controller/SmartBroadcastDevController.java +++ b/src/main/java/com/zhgd/xmgl/modules/broadcast/controller/SmartBroadcastDevController.java @@ -1,53 +1,28 @@ package com.zhgd.xmgl.modules.broadcast.controller; -import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.metadata.IPage; import com.zhgd.annotation.OperLog; +import com.zhgd.jeecg.common.api.vo.Result; +import com.zhgd.xmgl.modules.broadcast.entity.SmartBroadcastDev; +import com.zhgd.xmgl.modules.broadcast.entity.dto.SmartBroadcastDevDto; +import com.zhgd.xmgl.modules.broadcast.entity.vo.SmartBroadcastDevVo; +import com.zhgd.xmgl.modules.broadcast.service.ISmartBroadcastDevService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiImplicitParams; - -import java.util.HashMap; - +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.MapUtils; +import org.simpleframework.xml.core.Validate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; import springfox.documentation.annotations.ApiIgnore; import java.util.Arrays; +import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.zhgd.jeecg.common.api.vo.Result; -import com.zhgd.jeecg.common.system.query.QueryGenerator; -import com.zhgd.jeecg.common.util.oConvertUtils; -import org.apache.commons.collections.MapUtils; -import com.zhgd.xmgl.modules.broadcast.entity.SmartBroadcastDev; -import com.zhgd.xmgl.modules.broadcast.entity.vo.SmartBroadcastDevVo; -import com.zhgd.xmgl.modules.broadcast.entity.dto.SmartBroadcastDevDto; -import com.zhgd.xmgl.modules.broadcast.service.ISmartBroadcastDevService; -import org.springframework.context.annotation.Lazy; - -import org.simpleframework.xml.core.Validate; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import lombok.extern.slf4j.Slf4j; - -import org.jeecgframework.poi.excel.ExcelImportUtil; -import org.jeecgframework.poi.excel.def.NormalExcelConstants; -import org.jeecgframework.poi.excel.entity.ExportParams; -import org.jeecgframework.poi.excel.entity.ImportParams; -import org.jeecgframework.poi.excel.view.JeecgEntityExcelView; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.multipart.MultipartHttpServletRequest; -import org.springframework.web.servlet.ModelAndView; -import com.alibaba.fastjson.JSON; /** @@ -150,4 +125,21 @@ public class SmartBroadcastDevController { return Result.success(smartBroadcastDevService.queryById(id)); } + @ApiOperation(value = "批量删除智能广播设备", notes = "批量删除智能广播设备", httpMethod = "POST") + @ApiImplicitParam(name = "ids", value = "智能广播设备ID字符串(多个以,分割)", paramType = "body", required = true, dataType = "String") + @PostMapping(value = "/deleteBatch") + @Transactional(rollbackFor = Exception.class) + public Result deleteBatch(@ApiIgnore @RequestBody HashMap paramMap) { + String ids = MapUtils.getString(paramMap, "ids"); + Result result = new Result<>(); + if (ids == null || "".equals(ids.trim())) { + result.error500("参数不识别!"); + } else { + List idList = Arrays.asList(ids.split(",")); + smartBroadcastDevService.removeByIds(idList); + Result.success("删除成功!"); + } + return result; + } + } diff --git a/src/main/java/com/zhgd/xmgl/modules/risk/controller/RiskListPointController.java b/src/main/java/com/zhgd/xmgl/modules/risk/controller/RiskListPointController.java index fcb950bdd..588a81ebd 100644 --- a/src/main/java/com/zhgd/xmgl/modules/risk/controller/RiskListPointController.java +++ b/src/main/java/com/zhgd/xmgl/modules/risk/controller/RiskListPointController.java @@ -207,6 +207,7 @@ public class RiskListPointController { @ApiImplicitParam(name = "updates", value = "对象数组", paramType = "body", required = true, dataType = "Integer"), }) @PostMapping(value = "/batchSetField") + @Transactional(rollbackFor = Exception.class) public Result batchSetField(@RequestBody Map param) { List> updates = com.gexin.fastjson.JSON.parseObject(com.gexin.fastjson.JSON.toJSONString(param.get("updates"), SerializerFeature.WriteMapNullValue), new com.gexin.fastjson.TypeReference>>() { }); diff --git a/src/main/java/com/zhgd/xmgl/modules/risk/controller/RiskListSourceDataCenterController.java b/src/main/java/com/zhgd/xmgl/modules/risk/controller/RiskListSourceDataCenterController.java index eae737e6d..cd46731fd 100644 --- a/src/main/java/com/zhgd/xmgl/modules/risk/controller/RiskListSourceDataCenterController.java +++ b/src/main/java/com/zhgd/xmgl/modules/risk/controller/RiskListSourceDataCenterController.java @@ -25,7 +25,10 @@ import com.zhgd.xmgl.modules.risk.entity.bo.SourceCheckNumBo; import com.zhgd.xmgl.modules.risk.entity.vo.CountRisksByLevelVo; import com.zhgd.xmgl.modules.risk.entity.vo.HighRiskValByType; import com.zhgd.xmgl.modules.risk.entity.vo.RiskListSourceVo; -import com.zhgd.xmgl.modules.risk.service.*; +import com.zhgd.xmgl.modules.risk.service.IRiskListDetailService; +import com.zhgd.xmgl.modules.risk.service.IRiskListPotentialAccidentTypeService; +import com.zhgd.xmgl.modules.risk.service.IRiskListSourceService; +import com.zhgd.xmgl.modules.risk.service.IRiskListSourceUnbuiltService; import com.zhgd.xmgl.modules.xz.security.entity.XzSecurityQualityInspectionRecord; import com.zhgd.xmgl.modules.xz.security.service.IXzSecurityQualityInspectionRecordService; import com.zhgd.xmgl.util.ListUtils; @@ -177,85 +180,53 @@ public class RiskListSourceDataCenterController { } Date begin = DateUtil.parseDate(MapUtils.getString(param, "effectiveTimeEnd_begin")); Date end = DateUtil.parseDate(MapUtils.getString(param, "effectiveTimeBegin_end")); - List checkNumBos = getSourceCheckNumBo(sourceVos); + String projectSn = MapUtils.getString(param, "projectSn"); + List securityList = xzSecurityQualityInspectionRecordService.list(new LambdaQueryWrapper() + .eq(XzSecurityQualityInspectionRecord::getType, 10) + .eq(XzSecurityQualityInspectionRecord::getProjectSn, projectSn) + ); + List unbuilts = riskListSourceUnbuiltService.list(new LambdaQueryWrapper() + .in(RiskListSourceUnbuilt::getProjectSn, projectSn)); + Map>> sourceId2RegionId2SecurityListMap = securityList.stream() + .collect(Collectors.groupingBy( + XzSecurityQualityInspectionRecord::getEngineeringId, + Collectors.collectingAndThen( + Collectors.toList(), + records -> records.stream() + .filter(r -> Objects.nonNull(r.getRegionIds())) + .flatMap(record -> + Arrays.stream(record.getRegionIds().split(",")) + .map(String::trim) + .filter(regionId -> !regionId.isEmpty()) + .map(regionId -> new AbstractMap.SimpleEntry<>(regionId, record)) + ) + .collect(Collectors.groupingBy( + AbstractMap.SimpleEntry::getKey, + Collectors.mapping( + AbstractMap.SimpleEntry::getValue, + Collectors.toList() + )))))); + Map>> sourceId2RegionId2UnbuiltListMap = unbuilts.stream().collect(Collectors.groupingBy(RiskListSourceUnbuilt::getSourceId, + Collectors.groupingBy((RiskListSourceUnbuilt riskListSourceUnbuilt) -> Convert.toStr(riskListSourceUnbuilt.getResponsibleRegionId())))); //1. 计算有效期内的source的应排查、(未施工+已排查)数量 for (RiskListSourceVo sourceVo : sourceVos) { int shouldCheckedNum = 0; int checkedNum = 0; - Date b = DateUtil.compare((sourceVo.getEffectiveTimeBegin()), begin) < 0 ? sourceVo.getEffectiveTimeBegin() : begin; - Date e = DateUtil.compare(sourceVo.getEffectiveTimeEnd(), end) < 0 ? sourceVo.getEffectiveTimeEnd() : end; - if (Objects.equals(sourceVo.getCheckPeriod(), 1)) { - DateRange range = DateUtil.range(b, e, DateField.DAY_OF_YEAR); - for (DateTime time : range) { - shouldCheckedNum += sourceVo.getCheckNum(); - long count = checkNumBos.stream().filter(bo -> { - boolean b1 = isSameSourceAndTime(begin, end, sourceVo, bo, time); - return b1 && DateUtil.compare(bo.getDate(), DateUtil.offsetDay(time, 1)) < 0; - }).count(); - checkedNum += Math.min(sourceVo.getCheckNum(), count); - } - } else if (Objects.equals(sourceVo.getCheckPeriod(), 2)) { - DateRange range = DateUtil.range(DateUtil.beginOfWeek(b), DateUtil.offsetWeek(e, 1), DateField.DAY_OF_WEEK); - for (DateTime time : range) { - shouldCheckedNum += sourceVo.getCheckNum(); - long count = checkNumBos.stream().filter(bo -> { - boolean b1 = isSameSourceAndTime(begin, end, sourceVo, bo, time); - return b1 && DateUtil.compare(bo.getDate(), DateUtil.offsetWeek(time, 1)) < 0; - }).count(); - checkedNum += Math.min(sourceVo.getCheckNum(), count); - } - } else if (Objects.equals(sourceVo.getCheckPeriod(), 3)) { - DateRange range = DateUtil.range(DateUtil.beginOfMonth(b), DateUtil.offsetMonth(e, 1), DateField.MONTH); - for (DateTime time : range) { - shouldCheckedNum += sourceVo.getCheckNum(); - checkedNum += checkNumBos.stream().filter(bo -> { - boolean b1 = isSameSourceAndTime(begin, end, sourceVo, bo, time); - return b1 && DateUtil.compare(bo.getDate(), DateUtil.offsetMonth(time, 1)) < 0; - }).count(); - } - } else if (Objects.equals(sourceVo.getCheckPeriod(), 4)) { - // 获取起始日期所在季度的第一天 - DateTime currentQuarterStart = DateUtil.beginOfQuarter(begin); - // 循环直到当前季度的第一天晚于结束日期 - // 注意:这里需要确保 endDate 所在的季度被包含。 - // 可以判断 currentQuarterStart 是否在 endDate 所在季度的开始之前或相同 - while (!currentQuarterStart.isAfter(DateUtil.endOfQuarter(end))) { - shouldCheckedNum += sourceVo.getCheckNum(); - DateTime finalCurrentQuarterStart = currentQuarterStart; - checkedNum += checkNumBos.stream().filter(bo -> { - boolean b1 = isSameSourceAndTime(begin, end, sourceVo, bo, finalCurrentQuarterStart); - return b1 && DateUtil.compare(bo.getDate(), DateUtil.offsetMonth(finalCurrentQuarterStart, 3)) < 0; - }).count(); - currentQuarterStart = DateUtil.offsetMonth(currentQuarterStart, 3); - } - } else if (Objects.equals(sourceVo.getCheckPeriod(), 5)) { - // 找到起始日期所在的半年的第一个月的第一天 - DateTime currentHalfYearStart; - int startMonth = DateUtil.month(begin) + 1; // 月份是0-11,所以+1 - if (startMonth >= 1 && startMonth <= 6) { // 上半年 (1月-6月) - currentHalfYearStart = DateUtil.beginOfYear(begin); // 获取年份的开始,即1月1日 - } else { // 下半年 (7月-12月) - currentHalfYearStart = DateUtil.offsetMonth(DateUtil.beginOfYear(begin), 6); // 获取年份的开始,然后偏移6个月,即7月1日 - } - // 循环直到当前半年的第一天晚于结束日期所在的半年的最后一天 - // 或者简单判断 currentHalfYearStart 是否晚于 endDate - while (!currentHalfYearStart.isAfter(DateUtil.endOfYear(end))) { // 确保不超过endDate所在的年末 - // 进一步判断是否已经超出了endDate - if (currentHalfYearStart.isAfter(end) && DateUtil.month(currentHalfYearStart) + 1 > 6) { // 避免过度遍历到下一个年份的下半年 - break; - } - shouldCheckedNum += sourceVo.getCheckNum(); - DateTime finalCurrentHalfYearStart = currentHalfYearStart; - checkedNum += checkNumBos.stream().filter(bo -> { - boolean b1 = isSameSourceAndTime(begin, end, sourceVo, bo, finalCurrentHalfYearStart); - return b1 && DateUtil.compare(bo.getDate(), DateUtil.offsetMonth(DateUtil.beginOfYear(finalCurrentHalfYearStart), 6)) < 0; - }).count(); - // 移动到下一个半年的第一个月 - currentHalfYearStart = DateUtil.offsetMonth(currentHalfYearStart, 6); + List dateTimes = getDateTimes(begin, end, sourceVo); + for (DateTime time : dateTimes) { + shouldCheckedNum += sourceVo.getSpecificResponsibilityAreaIds().split(",").length * sourceVo.getCheckNum(); + String areaIds = sourceVo.getSpecificResponsibilityAreaIds(); + int count = 0; + if (StrUtil.isNotBlank(areaIds)) { + count = StrUtil.split(areaIds, ",").stream().map(regionId -> { + return riskListSourceService.getCalNum(sourceVo, regionId, time, Optional.ofNullable(sourceId2RegionId2SecurityListMap.get(sourceVo.getId())).map(m -> m.get(regionId)).orElse(null), + Optional.ofNullable(sourceId2RegionId2UnbuiltListMap.get(sourceVo.getId())).map(m -> m.get(regionId)).orElse(null)); + }).mapToInt(v -> v).sum(); } + checkedNum += Math.min(shouldCheckedNum, count); } sourceVo.setShouldCheckedNum(shouldCheckedNum); - sourceVo.setCheckedNum(checkedNum); + sourceVo.setAllCheckedNum(checkedNum); } //2. 统计执行率 // 临时Map,存储每个type的应排查总和和已排查总和 @@ -268,7 +239,7 @@ public class RiskListSourceDataCenterController { vo -> { // 为每个元素创建一个临时的Map return new HashMap() {{ put("totalShould", vo.getShouldCheckedNum()); - put("totalHave", vo.getCheckedNum()); + put("totalHave", vo.getAllCheckedNum()); }}; }, (map1, map2) -> { // 合并函数 @@ -300,6 +271,56 @@ public class RiskListSourceDataCenterController { return Result.success(voList); } + /** + * 获取危险源的时间列表 + * + * @param begin + * @param end + * @param sourceVo + * @return + */ + private List getDateTimes(Date begin, Date end, RiskListSourceVo sourceVo) { + Date b = DateUtil.compare((sourceVo.getEffectiveTimeBegin()), begin) > 0 ? sourceVo.getEffectiveTimeBegin() : begin; + Date e = DateUtil.compare(sourceVo.getEffectiveTimeEnd(), end) < 0 ? sourceVo.getEffectiveTimeEnd() : end; + List dateTimes = new ArrayList<>(); + if (Objects.equals(sourceVo.getCheckPeriod(), 1)) { + dateTimes = DateUtil.rangeToList(b, e, DateField.DAY_OF_YEAR); + } else if (Objects.equals(sourceVo.getCheckPeriod(), 2)) { + dateTimes = DateUtil.rangeToList(b, e, DateField.DAY_OF_WEEK); + } else if (Objects.equals(sourceVo.getCheckPeriod(), 3)) { + dateTimes = DateUtil.rangeToList(b, e, DateField.MONTH); + } else if (Objects.equals(sourceVo.getCheckPeriod(), 4)) { + // 获取起始日期所在季度的第一天 + DateTime currentQuarterStart = DateUtil.beginOfQuarter(begin); + dateTimes.add(currentQuarterStart); + while (!currentQuarterStart.isAfter(DateUtil.endOfQuarter(end))) { + currentQuarterStart = DateUtil.offsetMonth(currentQuarterStart, 3); + dateTimes.add(DateUtil.offsetMonth(currentQuarterStart, 3)); + } + } else if (Objects.equals(sourceVo.getCheckPeriod(), 5)) { + // 找到起始日期所在的半年的第一个月的第一天 + DateTime currentHalfYearStart; + int startMonth = DateUtil.month(begin) + 1; // 月份是0-11,所以+1 + if (startMonth >= 1 && startMonth <= 6) { // 上半年 (1月-6月) + currentHalfYearStart = DateUtil.beginOfYear(begin); // 获取年份的开始,即1月1日 + } else { // 下半年 (7月-12月) + currentHalfYearStart = DateUtil.offsetMonth(DateUtil.beginOfYear(begin), 6); // 获取年份的开始,然后偏移6个月,即7月1日 + } + dateTimes.add(currentHalfYearStart); + // 循环直到当前半年的第一天晚于结束日期所在的半年的最后一天 + while (!currentHalfYearStart.isAfter(DateUtil.endOfYear(end))) { // 确保不超过endDate所在的年末 + // 进一步判断是否已经超出了endDate + if (currentHalfYearStart.isAfter(end) && DateUtil.month(currentHalfYearStart) + 1 > 6) { // 避免过度遍历到下一个年份的下半年 + break; + } + // 移动到下一个半年的第一个月 + currentHalfYearStart = DateUtil.offsetMonth(currentHalfYearStart, 6); + dateTimes.add(currentHalfYearStart); + } + } + return dateTimes; + } + /** * 相同source并且在时间范围内 * @@ -360,80 +381,84 @@ public class RiskListSourceDataCenterController { } Date begin = DateUtil.parseDate(MapUtils.getString(param, "effectiveTimeEnd_begin")); Date end = DateUtil.parseDate(MapUtils.getString(param, "effectiveTimeBegin_end")); - List checkNumBos = getSourceCheckNumBo(sourceVos); + String projectSn = MapUtils.getString(param, "projectSn"); + List securityList = xzSecurityQualityInspectionRecordService.list(new LambdaQueryWrapper() + .eq(XzSecurityQualityInspectionRecord::getType, 10) + .eq(XzSecurityQualityInspectionRecord::getProjectSn, projectSn) + ); + List unbuilts = riskListSourceUnbuiltService.list(new LambdaQueryWrapper() + .in(RiskListSourceUnbuilt::getProjectSn, projectSn)); + Map>> sourceId2RegionId2SecurityListMap = securityList.stream() + .collect(Collectors.groupingBy( + XzSecurityQualityInspectionRecord::getEngineeringId, + Collectors.collectingAndThen( + Collectors.toList(), + records -> records.stream() + .filter(r -> Objects.nonNull(r.getRegionIds())) + .flatMap(record -> + Arrays.stream(record.getRegionIds().split(",")) + .map(String::trim) + .filter(regionId -> !regionId.isEmpty()) + .map(regionId -> new AbstractMap.SimpleEntry<>(regionId, record)) + ) + .collect(Collectors.groupingBy( + AbstractMap.SimpleEntry::getKey, + Collectors.mapping( + AbstractMap.SimpleEntry::getValue, + Collectors.toList() + )))))); + Map>> sourceId2RegionId2UnbuiltListMap = unbuilts.stream().collect(Collectors.groupingBy(RiskListSourceUnbuilt::getSourceId, + Collectors.groupingBy((RiskListSourceUnbuilt riskListSourceUnbuilt) -> Convert.toStr(riskListSourceUnbuilt.getResponsibleRegionId())))); //1. 计算有效期内的source的每天的应排查、(未施工+已排查)数量 List sourceDateVos = new ArrayList<>(); for (RiskListSourceVo sourceVo : sourceVos) { - Date b = DateUtil.compare((sourceVo.getEffectiveTimeBegin()), begin) < 0 ? sourceVo.getEffectiveTimeBegin() : begin; - Date e = DateUtil.compare(sourceVo.getEffectiveTimeEnd(), end) < 0 ? sourceVo.getEffectiveTimeEnd() : end; - if (Objects.equals(sourceVo.getCheckPeriod(), 1)) { - DateRange range = DateUtil.range(b, DateUtil.offsetDay(e, 1), DateField.DAY_OF_YEAR); - for (DateTime time : range) { - addRiskByDateBo(sourceDateVos, time, DateUtil.offsetWeek(time, 1), sourceVo, checkNumBos, begin, end); - } - } else if (Objects.equals(sourceVo.getCheckPeriod(), 2)) { - DateRange range = DateUtil.range(DateUtil.beginOfWeek(b), DateUtil.offsetWeek(e, 1), DateField.DAY_OF_WEEK); - for (DateTime time : range) { - addRiskByDateBo(sourceDateVos, time, DateUtil.offsetWeek(time, 1), sourceVo, checkNumBos, begin, end); - } - } else if (Objects.equals(sourceVo.getCheckPeriod(), 3)) { - DateRange range = DateUtil.range(DateUtil.beginOfMonth(b), DateUtil.offsetMonth(e, 1), DateField.MONTH); - for (DateTime time : range) { - addRiskByDateBo(sourceDateVos, time, DateUtil.offsetMonth(time, 1), sourceVo, checkNumBos, begin, end); - } - } else if (Objects.equals(sourceVo.getCheckPeriod(), 4)) { - // 获取起始日期所在季度的第一天 - DateTime currentQuarterStart = DateUtil.beginOfQuarter(begin); - // 循环直到当前季度的第一天晚于结束日期 - // 注意:这里需要确保 endDate 所在的季度被包含。 - // 可以判断 currentQuarterStart 是否在 endDate 所在季度的开始之前或相同 - while (!currentQuarterStart.isAfter(DateUtil.endOfQuarter(end))) { - addRiskByDateBo(sourceDateVos, currentQuarterStart, DateUtil.offsetMonth(currentQuarterStart, 3), sourceVo, checkNumBos, begin, end); - currentQuarterStart = DateUtil.offsetMonth(currentQuarterStart, 3); - } - } else if (Objects.equals(sourceVo.getCheckPeriod(), 5)) { - // 找到起始日期所在的半年的第一个月的第一天 - DateTime currentHalfYearStart; - int startMonth = DateUtil.month(begin) + 1; // 月份是0-11,所以+1 - if (startMonth >= 1 && startMonth <= 6) { // 上半年 (1月-6月) - currentHalfYearStart = DateUtil.beginOfYear(begin); // 获取年份的开始,即1月1日 - } else { // 下半年 (7月-12月) - currentHalfYearStart = DateUtil.offsetMonth(DateUtil.beginOfYear(begin), 6); // 获取年份的开始,然后偏移6个月,即7月1日 - } - // 循环直到当前半年的第一天晚于结束日期所在的半年的最后一天 - // 或者简单判断 currentHalfYearStart 是否晚于 endDate - while (!currentHalfYearStart.isAfter(DateUtil.endOfYear(end))) { // 确保不超过endDate所在的年末 - // 进一步判断是否已经超出了endDate - if (currentHalfYearStart.isAfter(end) && DateUtil.month(currentHalfYearStart) + 1 > 6) { // 避免过度遍历到下一个年份的下半年 - break; - } - addRiskByDateBo(sourceDateVos, currentHalfYearStart, DateUtil.offsetMonth(currentHalfYearStart, 6), sourceVo, checkNumBos, begin, end); - // 移动到下一个半年的第一个月 - currentHalfYearStart = DateUtil.offsetMonth(currentHalfYearStart, 6); + List dateTimes = getDateTimes(begin, end, sourceVo); + for (DateTime time : dateTimes) { + int shouldCheckedNum = sourceVo.getSpecificResponsibilityAreaIds().split(",").length * sourceVo.getCheckNum(); + String areaIds = sourceVo.getSpecificResponsibilityAreaIds(); + int count = 0; + if (StrUtil.isNotBlank(areaIds)) { + count = StrUtil.split(areaIds, ",").stream().map(regionId -> { + return riskListSourceService.getCalNum(sourceVo, regionId, time, Optional.ofNullable(sourceId2RegionId2SecurityListMap.get(sourceVo.getId())).map(m -> m.get(regionId)).orElse(null), + Optional.ofNullable(sourceId2RegionId2UnbuiltListMap.get(sourceVo.getId())).map(m -> m.get(regionId)).orElse(null)); + }).mapToInt(v -> v).sum(); } + int checkedNum = Math.min(shouldCheckedNum, count); + RiskByDateBo newbo = new RiskByDateBo(); + newbo.setDate(time); + newbo.setShouldCheckedNum(shouldCheckedNum); + newbo.setAllCheckedNum(checkedNum); + newbo.setSourceId(sourceVo.getId()); + sourceDateVos.add(newbo); } } + //2. 统计每日执行率 // 临时Map,存储每个type的应排查总和和已排查总和 - Map> sumsByType = sourceDateVos.stream().collect(Collectors.groupingBy(RiskByDateBo::getDate, - Collectors.reducing( - new HashMap() {{ - put("totalShould", 0); - put("totalHave", 0); - }}, // 初始值 - vo -> { // 为每个元素创建一个临时的Map - return new HashMap() {{ - put("totalShould", vo.getShouldCheckedNum()); - put("totalHave", vo.getCheckedNum()); - }}; - }, - (map1, map2) -> { // 合并函数 - map1.merge("totalShould", map2.get("totalShould"), (integer, integer2) -> NumberUtil.add(integer, integer2).intValue()); - map1.merge("totalHave", map2.get("totalHave"), (integer, integer2) -> NumberUtil.add(integer, integer2).intValue()); - return map1; - } - ))); - + Map> sumsByType = sourceDateVos.stream() + .collect(Collectors.groupingBy( + RiskByDateBo::getDate, + Collectors.collectingAndThen( + Collectors.toList(), + list -> { + int totalShould = list.stream().mapToInt(RiskByDateBo::getShouldCheckedNum).sum(); + int totalHave = list.stream().mapToInt(RiskByDateBo::getAllCheckedNum).sum(); + return new HashMap() {{ + put("totalShould", totalShould); + put("totalHave", totalHave); + }}; + } + ) + )); + List dateTimes = DateUtil.rangeToList(begin, end, DateField.DAY_OF_YEAR); + for (DateTime dateTime : dateTimes) { + if (!sumsByType.containsKey(dateTime)) { + sumsByType.put(dateTime, new HashMap() {{ + put("totalShould", 0); + put("totalHave", 0); + }}); + } + } // 计算比率并转换为目标输出格式 List voList = sumsByType.entrySet().stream() .sorted(Comparator.comparing(Map.Entry::getKey)) @@ -452,8 +477,7 @@ public class RiskListSourceDataCenterController { vo.setX(DateUtil.formatDate(type)); vo.setY(Convert.toStr(sumRatio)); return vo; - }) - .collect(Collectors.toList()); + }).sorted(Comparator.comparing(TrendOneVo::getX)).collect(Collectors.toList()); return Result.success(voList); } diff --git a/src/main/java/com/zhgd/xmgl/modules/risk/entity/RiskListSource.java b/src/main/java/com/zhgd/xmgl/modules/risk/entity/RiskListSource.java index ed45c99ae..f7eeafce3 100644 --- a/src/main/java/com/zhgd/xmgl/modules/risk/entity/RiskListSource.java +++ b/src/main/java/com/zhgd/xmgl/modules/risk/entity/RiskListSource.java @@ -214,7 +214,7 @@ public class RiskListSource implements Serializable { * 活动频率 */ @ApiModelProperty(value = "活动频率") - private java.lang.Integer activityFrequency; + private java.lang.String activityFrequency; /** * 设备设施属性 */ diff --git a/src/main/java/com/zhgd/xmgl/modules/risk/entity/bo/RiskByDateBo.java b/src/main/java/com/zhgd/xmgl/modules/risk/entity/bo/RiskByDateBo.java index 79d40b89a..e239808fa 100644 --- a/src/main/java/com/zhgd/xmgl/modules/risk/entity/bo/RiskByDateBo.java +++ b/src/main/java/com/zhgd/xmgl/modules/risk/entity/bo/RiskByDateBo.java @@ -19,5 +19,9 @@ public class RiskByDateBo { * 已排查数量(隐患+排查记录) */ private java.lang.Integer checkedNum; + /** + * 已排查数量(隐患+排查记录+未施工数量) + */ + private java.lang.Integer allCheckedNum; private Long sourceId; } diff --git a/src/main/java/com/zhgd/xmgl/modules/risk/entity/vo/RiskListSourceVo.java b/src/main/java/com/zhgd/xmgl/modules/risk/entity/vo/RiskListSourceVo.java index 93cc57630..88c4827de 100644 --- a/src/main/java/com/zhgd/xmgl/modules/risk/entity/vo/RiskListSourceVo.java +++ b/src/main/java/com/zhgd/xmgl/modules/risk/entity/vo/RiskListSourceVo.java @@ -6,7 +6,10 @@ import com.zhgd.xmgl.modules.risk.entity.RiskListSource; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriter; import java.math.BigDecimal; +import java.util.Iterator; import java.util.List; @Data @@ -22,7 +25,7 @@ public class RiskListSourceVo extends RiskListSource { @ApiModelProperty(value = "风险值") private BigDecimal riskVal; /** - * 应排查数量 + * 应排查数量(隐患+排查记录+未施工数量) */ @JsonIgnore private java.lang.Integer shouldCheckedNum; @@ -31,6 +34,11 @@ public class RiskListSourceVo extends RiskListSource { */ @ApiModelProperty(value = "已排查数量(隐患+排查记录)") private java.lang.Integer checkedNum; + /** + * 已排查数量(隐患+排查记录+未施工数量) + */ + @JsonIgnore + private java.lang.Integer allCheckedNum; /** * 未施工数量 */ @@ -118,4 +126,16 @@ public class RiskListSourceVo extends RiskListSource { @ApiModelProperty(value = "具体责任区域名称(多个,分割)") @TableField(exist = false) private java.lang.String specificResponsibilityAreaNames; + + public static void main(String[] args) { + // 检查 WEBP 编码器是否可用 + Iterator writers = ImageIO.getImageWritersByMIMEType("image/webp"); + if (writers.hasNext()) { + System.out.println("✅ WEBP encoder is available!"); + ImageWriter writer = writers.next(); + System.out.println("Encoder: " + writer.getClass().getName()); + } else { + System.out.println("❌ WEBP encoder NOT available."); + } + } } diff --git a/src/main/java/com/zhgd/xmgl/modules/risk/service/IRiskListSourceService.java b/src/main/java/com/zhgd/xmgl/modules/risk/service/IRiskListSourceService.java index 7c398e863..c0d0f269b 100644 --- a/src/main/java/com/zhgd/xmgl/modules/risk/service/IRiskListSourceService.java +++ b/src/main/java/com/zhgd/xmgl/modules/risk/service/IRiskListSourceService.java @@ -4,12 +4,16 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.zhgd.xmgl.base.entity.vo.SectorOneVo; import com.zhgd.xmgl.modules.risk.entity.RiskListSource; +import com.zhgd.xmgl.modules.risk.entity.RiskListSourceUnbuilt; import com.zhgd.xmgl.modules.risk.entity.dto.RiskListSourceDto; import com.zhgd.xmgl.modules.risk.entity.vo.CountRisksByLevelVo; import com.zhgd.xmgl.modules.risk.entity.vo.RiskListSourceVo; import com.zhgd.xmgl.modules.risk.entity.vo.RiskUndoneVo; +import com.zhgd.xmgl.modules.xz.security.entity.XzSecurityQualityInspectionRecord; +import org.jetbrains.annotations.Nullable; import java.math.BigDecimal; +import java.util.Date; import java.util.HashMap; import java.util.List; @@ -36,6 +40,18 @@ public interface IRiskListSourceService extends IService { */ List queryList(HashMap param); + /** + * 计算完成的数量 + * + * @param sourceVo + * @param regionId + * @param date + * @param securityList + * @param unbuilts + * @return + */ + int getCalNum(RiskListSourceVo sourceVo, String regionId, Date date, @Nullable List securityList, @Nullable List unbuilts); + /** * 添加管控清单危险源信息 * diff --git a/src/main/java/com/zhgd/xmgl/modules/risk/service/impl/RiskListPointServiceImpl.java b/src/main/java/com/zhgd/xmgl/modules/risk/service/impl/RiskListPointServiceImpl.java index b5b1978bb..4709c21e3 100644 --- a/src/main/java/com/zhgd/xmgl/modules/risk/service/impl/RiskListPointServiceImpl.java +++ b/src/main/java/com/zhgd/xmgl/modules/risk/service/impl/RiskListPointServiceImpl.java @@ -1,5 +1,6 @@ package com.zhgd.xmgl.modules.risk.service.impl; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -14,6 +15,7 @@ import com.zhgd.xmgl.modules.quality.entity.vo.QualityRegionVo; import com.zhgd.xmgl.modules.quality.service.IQualityRegionService; import com.zhgd.xmgl.modules.risk.entity.RiskListPoint; import com.zhgd.xmgl.modules.risk.entity.RiskListSource; +import com.zhgd.xmgl.modules.risk.entity.dto.RiskListSourceDto; import com.zhgd.xmgl.modules.risk.entity.vo.RiskListPointVo; import com.zhgd.xmgl.modules.risk.entity.vo.RiskListSourceVo; import com.zhgd.xmgl.modules.risk.mapper.RiskListPointMapper; @@ -103,6 +105,9 @@ public class RiskListPointServiceImpl extends ServiceImpl Objects.equals(riskListSource.getPointId(), riskListPointVo.getId())).count()); + } if (Objects.equals(isMySourceToDo, 1) || Objects.equals(isMySourceToDoForWorkable, 1)) { Long regionIds = MapUtils.getLong(param, "regionIds"); List sourceVos = riskListSourceService.queryList(new MapBuilder() @@ -158,6 +163,28 @@ public class RiskListPointServiceImpl extends ServiceImpl sources = riskListSourceService.list(new LambdaQueryWrapper() + .eq(RiskListSource::getPointId, point.getId())); + for (RiskListSource source : sources) { + source.setAreaLocation(point.getAreaLocation()); + source.setRemark(point.getRiskSituationDescription()); + source.setActivityFrequency(point.getActivityFrequency()); + source.setEffectiveTimeBegin(point.getEffectiveTimeBegin()); + source.setEffectiveTimeEnd(point.getEffectiveTimeEnd()); + RiskListSourceDto sourceDto = new RiskListSourceDto(); + BeanUtil.copyProperties(source, sourceDto); + sourceDto.setSpecificResponsibilityAreaIds(point.getSpecificResponsibilityAreaIds()); + riskListSourceService.edit(sourceDto); + } } @Override diff --git a/src/main/java/com/zhgd/xmgl/modules/risk/service/impl/RiskListSourceServiceImpl.java b/src/main/java/com/zhgd/xmgl/modules/risk/service/impl/RiskListSourceServiceImpl.java index ad2f8dbf9..90a417fed 100644 --- a/src/main/java/com/zhgd/xmgl/modules/risk/service/impl/RiskListSourceServiceImpl.java +++ b/src/main/java/com/zhgd/xmgl/modules/risk/service/impl/RiskListSourceServiceImpl.java @@ -236,7 +236,7 @@ public class RiskListSourceServiceImpl extends ServiceImpl securityList1 = securityList.stream().filter(r -> StrUtil.isNotBlank(r.getInspectTime()) && Objects.equals(r.getEngineeringId(), sourceVo.getId())).collect(Collectors.toList()); List unbuilts1 = unbuilts.stream().filter(r -> r.getInspectionTime() != null && Objects.equals(r.getSourceId(), sourceVo.getId())).collect(Collectors.toList()); return StrUtil.split(sourceVo.getSpecificResponsibilityAreaIds(), ",").stream().map(Convert::toLong).filter(regionId -> { - int totalCalNum = getCalNum(sourceVo, String.valueOf(regionId), securityList1, unbuilts1); + int totalCalNum = getCalNum(sourceVo, String.valueOf(regionId), new Date(), securityList1, unbuilts1); int remainingNum = sourceVo.getCheckNum() - totalCalNum; return remainingNum > 0; }).collect(Collectors.toList()); @@ -260,30 +260,26 @@ public class RiskListSourceServiceImpl extends ServiceImpl securityList, List unbuilts) { - String today = DateUtil.today(); - Date date = new Date(); + @Override + public int getCalNum(RiskListSourceVo sourceVo, String regionId, Date date, List securityList, List unbuilts) { + if (securityList == null) { + securityList = new ArrayList<>(); + } + if (unbuilts == null) { + unbuilts = new ArrayList<>(); + } String year = DateUtil.format(date, "yyyy"); int totalCalNum = 0; if (Objects.equals(sourceVo.getCheckPeriod(), 1)) { int count = (int) securityList.stream().filter(r -> DateUtil.isSameDay(DateUtil.parseDate(r.getInspectTime()), date) && isContainRegion(r, regionId)).count(); - int count1 = (int) unbuilts.stream().filter(r -> DateUtil.formatDate(r.getInspectionTime()).startsWith(today) && isEqualRegion(regionId, r)).count(); + int count1 = (int) unbuilts.stream().filter(r -> DateUtil.formatDate(r.getInspectionTime()).startsWith(DateUtil.formatDate(date)) && isEqualRegion(regionId, r)).count(); totalCalNum += Math.min(count + count1, sourceVo.getCheckNum()); } else if (Objects.equals(sourceVo.getCheckPeriod(), 2)) { int count = (int) securityList.stream().filter(r -> DateUtil.isSameWeek(DateUtil.parseDate(r.getInspectTime()), date, true) && isContainRegion(r, regionId)).count(); diff --git a/src/main/resources/lib/webp-imageio-core-0.1.3.jar b/src/main/resources/lib/webp-imageio-core-0.1.3.jar new file mode 100644 index 000000000..f369c02ae Binary files /dev/null and b/src/main/resources/lib/webp-imageio-core-0.1.3.jar differ