Compare commits

..

4 Commits

Author SHA1 Message Date
alsszer 4812cc8407 feat(device): 新增批量PDF导出功能并更新服务配置
- 新增BatchExportRequest和BatchPdfWrapper数据传输对象
- 实现DeviceInstallInfoController中的getPdfBatch接口支持批量PDF下载
- 添加ExportTask类用于管理导出任务状态
- 配置PDFGenerator工具类的批量处理功能
- 注入ExecutorService线程池支持异步任务执行
- 更新application-dev.yml中的数据库、ES和Redis连接地址
- 调整Hibernate SQL日志输出配置
- 优化PDF生成过程中的压缩和文件名处理
- 在告警消息中增加设备位置信息
4 weeks ago
alsszer 34c2a5f8b4 feat(plugin): 优化EMQX插件功能并增强设备管理
- 在application.yml中将运行模式从prod改为dev
- 在application-dev.yml中添加generate_statistics配置项以关闭统计信息
- 在AuthVerticle.java中添加MQTT ACL主题路径转换逻辑
- 在DeviceInfoDataCache.java和DeviceInfoDataImpl.java中增加设备状态查询方法
- 在DeviceInfoExpordVo和DeviceInfoImportVo中新增site字段用于设备位置导出导入
- 在DeviceInstallInfo.java中添加多个图片相关字段如泡沫水测漏图片、设备信息图片、门牌号图片
- 在DeviceInstallInfoDataImpl.java中实现findById方法
- 在DeviceInstallInfoExpordVo和DeviceInstallInfoVo中添加切断阀编号和报警器编号导出功能
- 在DeviceInstallInfoServiceImpl.java中完善安装信息查询逻辑
- 在DeviceManagerServiceImpl.java中支持设备状态查询参数
- 在DeviceQueryBo.java中新增deviceStatus字段用于设备状态筛选
- 在feijialuo-emqx-plugin中优化线程池配置并提升并发处理能力
- 在iot-iita-emqx-plugin中调整设备识别规则适配不同平台需求
2 months ago
alsszer 145fc94b34 事件配置设备报警后群发消息BUG修复 5 months ago
alsszer db19114ba7 事件配置设备报警后群发消息BUG修复 6 months ago

@ -77,6 +77,8 @@ public class DeviceInstallInfo extends TenantModel implements Owned<String>,Seri
private String workingOfTheDetectorImage;
// @ApiModelProperty(value = "测漏图片")
private String sideLeakageImage;
// @ApiModelProperty(value = "泡沫水测漏图片")
private String formSideLeakageImage;
// @ApiModelProperty(value = "点火图片")
private String ignitionPictureImage;
// @ApiModelProperty(value = "装完成全景图片")
@ -87,6 +89,10 @@ public class DeviceInstallInfo extends TenantModel implements Owned<String>,Seri
private String workOrderImage;
//@ApiModelProperty(value = "打孔图片")
private String punchingImage;
//@ApiModelProperty(value = "设备信息图片")
private String deviceInfoImage;
//@ApiModelProperty(value = "门牌号图片")
private String houseNumberImage;
// @ApiModelProperty(value = "用户id")
private String uid;
// @ApiModelProperty(value = "租户编号")

@ -38,4 +38,6 @@ public interface IAlertConfigData extends ICommonData<AlertConfig, Long> {
List<AlertConfig> findByUidAndRuleInfoIdAndDeviceName(String uid, String ruleInfoId,String deviceName);
Paging<AlertConfig> selectAlertConfigPage(PageRequest<AlertConfig> request);
List<AlertConfig> findByRuleInfoIdAndDeviceName(String ruleInfoId,String deviceName);
}

@ -114,6 +114,9 @@ public interface IDeviceInfoData extends IOwnedData<DeviceInfo, String> {
* @param page
* @param size
*/
Paging<DeviceInfo> findByConditions(String name,String uid, String subUid, String productKey,
String groupId, Boolean online, String keyword,
int page, int size,Long areaDepeId,String startTime,String endTime,String deviceName,Integer deviceStatus);
Paging<DeviceInfo> findByConditions(String name,String uid, String subUid, String productKey,
String groupId, Boolean online, String keyword,
int page, int size,Long areaDepeId,String startTime,String endTime,String deviceName);
@ -199,4 +202,6 @@ public interface IDeviceInfoData extends IOwnedData<DeviceInfo, String> {
List<DeviceInfo> findNeverUsedDevices();
Long findNeverUsedDevices(DeviceInfo deviceInfo,Boolean flag);
List<AlertStatDTO> getDeviceRecord();
List<DeviceInfo> findByDeptAreaIdAndProductKeyAndDeviceName(Long areaDeptId, String productKey, String deviceName);
}

@ -33,4 +33,6 @@ public interface IDeviceInstallInfoData extends IOwnedData<DeviceInstallInfo, St
DeviceInstallInfo findByDeviceNameAndUid(String deviceName,String uid);
DeviceInstallInfo findByDeviceNameAndUidAndState(String deviceName,String uid,Integer state);
DeviceInstallInfo findById(String id);
}

@ -85,4 +85,6 @@ public interface ISysRoleData extends ICommonData<SysRole, Long> {
List<SysRole> selectRoleList(SysRole role);
List<SysRole> findByUserId(Long id);
List<SysRole> selectByTenantId(Long tenantId);
}

@ -127,6 +127,12 @@ public class DeviceInfoDataCache implements IDeviceInfoData, SmartInitializingSi
public List<AlertStatDTO> getDeviceRecord() {
return deviceInfoData.getDeviceRecord();
}
@Override
public List<DeviceInfo> findByDeptAreaIdAndProductKeyAndDeviceName(Long deviceId, String productKey, String deviceName) {
return deviceInfoData.findByDeptAreaIdAndProductKeyAndDeviceName(deviceId,productKey,deviceName);
}
/**
*
*/
@ -238,6 +244,12 @@ public class DeviceInfoDataCache implements IDeviceInfoData, SmartInitializingSi
String groupId, Boolean state, String keyword, int page, int size,Long deptAreaId,String startTime,String endTime,String deviceName) {
return deviceInfoData.findByConditions(name,uid, subUid, productKey, groupId, state, keyword, page, size,deptAreaId,startTime,endTime,deviceName);
}
@Override
public Paging<DeviceInfo> findByConditions(String name,String uid, String subUid, String productKey,
String groupId, Boolean state, String keyword, int page, int size,Long deptAreaId,String startTime,String endTime,String deviceName,Integer deviceStatus) {
return deviceInfoData.findByConditions(name,uid, subUid, productKey, groupId, state, keyword, page, size,deptAreaId,startTime,endTime,deviceName,deviceStatus);
}
@Override
public Paging<DeviceInfo> findByConditionsExcel(String name,String uid, String subUid, String productKey,
String groupId, Boolean state, String keyword, int page, int size,Long deptAreaId,String startTime,String endTime) {

@ -176,6 +176,18 @@ public class DeviceInfoPropertyDataCache implements IDeviceInfoData {
public List<AlertStatDTO> getDeviceRecord() {
return deviceInfoData.getDeviceRecord();
}
@Override
public List<DeviceInfo> findByDeptAreaIdAndProductKeyAndDeviceName(Long areaDeptId, String productKey, String deviceName) {
List<DeviceInfo> deviceInfo = deviceInfoData.findByDeptAreaIdAndProductKeyAndDeviceName(areaDeptId, productKey, deviceName);
if (deviceInfo == null) {
return null;
}
deviceInfo.forEach(deviceInfo1 -> deviceInfo1.setProperty(getProperties(deviceInfo1.getDeviceId())));
// deviceInfo.setProperty(getProperties(deviceInfo.getDeviceId()));
return deviceInfo;
}
@Override
public List<DeviceInfo> findByParentId(String parentId) {
return deviceInfoData.findByParentId(parentId);
@ -190,6 +202,11 @@ public class DeviceInfoPropertyDataCache implements IDeviceInfoData {
public Paging<DeviceInfo> findByConditions(String name,String uid, String subUid, String productKey, String groupId, Boolean online, String keyword, int page, int size,Long deptAreaId,String startTime,String endTime,String deviceName) {
return deviceInfoData.findByConditions(name,uid, subUid, productKey, groupId, online, keyword, page, size,deptAreaId,startTime,endTime,deviceName);
}
@Override
public Paging<DeviceInfo> findByConditions(String name,String uid, String subUid, String productKey, String groupId, Boolean online, String keyword, int page, int size,Long deptAreaId,String startTime,String endTime,String deviceName,Integer deviceStatus) {
return deviceInfoData.findByConditions(name,uid, subUid, productKey, groupId, online, keyword, page, size,deptAreaId,startTime,endTime,deviceName,deviceStatus);
}
@Override
public Paging<DeviceInfo> findByConditionsExcel(String name,String uid, String subUid, String productKey, String groupId, Boolean online, String keyword, int page, int size,Long deptAreaId,String startTime,String endTime) {
return deviceInfoData.findByConditionsExcel(name,uid, subUid, productKey, groupId, online, keyword, page, size,deptAreaId,startTime,endTime);

@ -41,4 +41,6 @@ public interface AlertConfigRepository extends JpaRepository<TbAlertConfig, Long
List<TbAlertConfig> findByRuleInfoIdAndEnable(String ruleInfoId,Boolean enable);
List<TbAlertConfig> findByDeviceNameAndEnable(String deviceName,Boolean enable);
List<TbAlertConfig> findByUidAndRuleInfoIdAndDeviceNameAndEnable(String uid, String ruleInfoId,String deviceName,Boolean enable);
List<TbAlertConfig> findByRuleInfoIdAndDeviceNameAndEnable(String ruleInfoId, String deviceName, Boolean enable);
}

@ -39,4 +39,6 @@ public interface DeviceInfoRepository extends JpaRepository<TbDeviceInfo, String
List<TbDeviceInfo> findByDeptAreaIdAndProductKey(Long deptAreaId,String productKey);
long countByUid(String uid);
long countByUidAndState(String uid,String state);
List<TbDeviceInfo> findByDeptAreaIdAndProductKeyAndDeviceName(Long deptAreaId, String productKey, String deviceName);
}

@ -86,6 +86,8 @@ public class TbDeviceInstallInfo extends BaseEntity implements TenantAware {
private String workingOfTheDetectorImage;
@ApiModelProperty(value = "测漏图片")
private String sideLeakageImage;
@ApiModelProperty(value = "泡沫水测漏图片")
private String formSideLeakageImage;
@ApiModelProperty(value = "点火图片")
private String ignitionPictureImage;
@ApiModelProperty(value = "装完成全景图片")
@ -98,6 +100,10 @@ public class TbDeviceInstallInfo extends BaseEntity implements TenantAware {
private String punchingImage;
@ApiModelProperty(value = "装电源线照片")
private String fiexImage;
@ApiModelProperty(value = "设备信息图片")
private String deviceInfoImage;
@ApiModelProperty(value = "门牌号图片")
private String houseNumberImage;
@ApiModelProperty(value = "用户id")
private String uid;

@ -82,6 +82,12 @@ public class AlertConfigDataImpl implements IAlertConfigData, IJPACommData<Alert
.findAll(buildQueryCondition(request.getData()), PageBuilder.toPageable(request));
return new Paging<>(alertConfigPage.getTotalElements(), MapstructUtils.convert(alertConfigPage.getContent(), AlertConfig.class));
}
@Override
public List<AlertConfig> findByRuleInfoIdAndDeviceName(String ruleInfoId,String deviceName) {
return MapstructUtils.convert(alertConfigRepository.findByRuleInfoIdAndDeviceNameAndEnable(ruleInfoId,deviceName,true),AlertConfig.class);
}
@Override
public List<AlertConfig> findByUidAndRuleInfoId(String uid, String ruleInfoId) {
return MapstructUtils.convert(alertConfigRepository.findByUidAndRuleInfoId(uid, ruleInfoId), AlertConfig.class);

@ -513,6 +513,11 @@ public class DeviceInfoDataImpl implements IDeviceInfoData, IJPACommData<DeviceI
}
@Override
public List<DeviceInfo> findByDeptAreaIdAndProductKeyAndDeviceName(Long deptAreaId, String productKey, String deviceName) {
return parseVoToDto(deviceInfoRepository.findByDeptAreaIdAndProductKeyAndDeviceName(deptAreaId, productKey, deviceName));
}
@Override
public Paging<DeviceInfo> findByConditionsExcel(String name, String uid, String subUid,
String productKey, String groupId,
@ -629,6 +634,143 @@ public class DeviceInfoDataImpl implements IDeviceInfoData, IJPACommData<DeviceI
return new Paging<>(total, deviceInfos);
}
@Override
public Paging<DeviceInfo> findByConditions(String name, String uid, String subUid,
String productKey, String groupId,
Boolean online, String keyword,
int page, int size, Long deptAreaId,String startTime,String endTime,String deviceName,Integer deviceStatus) {
JPAQuery<TbDeviceInfo> query = jpaQueryFactory.selectFrom(tbDeviceInfo);
// 根据groupId, 如果groupId存在则关联查询TbDeviceGroupMapping, 根据groupId,查询对应的devices
if (StringUtils.isNotBlank(groupId)) {
if(!groupId.equals("0")){
query.join(tbDeviceGroupMapping).on(tbDeviceGroupMapping.deviceId.eq(tbDeviceInfo.deviceId));
// query.where(tbDeviceGroupMapping.groupId.eq(groupId));
query.where(tbDeviceGroupMapping.groupId.eq(groupId));
}
}else{
query.join(tbDeviceGroupMapping).on(tbDeviceGroupMapping.deviceId.eq(tbDeviceInfo.deviceId));
query.where(tbDeviceGroupMapping.groupId.isNull());
}
if (StringUtils.isNotBlank(uid)) {
query.where(tbDeviceInfo.uid.eq(uid));
}
if (StringUtils.isNotBlank(name)) {
query.where(tbDeviceInfo.name.like("%" + name + "%"));
}
if (StringUtils.isNotBlank(deviceName)) {
query.where(tbDeviceInfo.deviceName.like("%" + deviceName + "%"));
}
System.out.println(TenantHelper.getTenantId());
if (ObjectUtil.isNotNull(TenantHelper.getTenantId()) && (!LoginHelper.isSuperAdmin() || TenantHelper.getTenantId()!=0)) {
query.where(tbDeviceInfo.tenantId.eq(TenantHelper.getTenantId()));
}
if (ObjectUtil.isNotNull(deptAreaId)) {
/* List<Long> areaIds;
// if (Objects.nonNull(user) && Objects.nonNull(user.getDeptAreaId())) {
Long areaId = deptAreaId;
List<SysDept> depts = sysDeptData.findByDeptId(areaId);
areaIds = StreamUtils.toList(depts, SysDept::getId);
areaIds.add(areaId);
if (ObjectUtil.isNotEmpty(areaIds)) {*/
if(deptAreaId != 0) {
query.where(tbDeviceInfo.deptAreaId.in(deptAreaId));
} else {
if(ObjectUtil.isNotEmpty(LoginHelper.getUserId())) {
SysUser user = isSysUserData.findById(LoginHelper.getUserId());
if (ObjectUtil.isNotNull(user)) {
if (ObjectUtil.isNotNull(user.getDeptAreaId())) {
Long deptAreaId1 = user.getDeptAreaId();
// 根据部门id获取部门对象
SysDept dept = sysDeptData.findById(deptAreaId1);
if(dept.getParentId()!=0l){
// 说明为子部门 筛选条件为部门id
query.where(tbDeviceInfo.deptAreaId.eq(deptAreaId1));
}
}
}
}
}
// query.where(tbDeviceInfo.deptAreaId.in(deptAreaId));
// }
}else{
if(!LoginHelper.isSuperAdmin()) {
if(ObjectUtil.isNotEmpty(LoginHelper.getUserId())) {
SysUser user = isSysUserData.findById(LoginHelper.getUserId());
if (ObjectUtil.isNotNull(user)) {
if (ObjectUtil.isNotNull(user.getDeptAreaId())) {
/* List<Long> areaIds;
// if (Objects.nonNull(user) && Objects.nonNull(user.getDeptAreaId())) {
Long areaId = user.getDeptAreaId();
List<SysDept> depts = sysDeptData.findByDeptId(areaId);
areaIds = StreamUtils.toList(depts, SysDept::getId);
areaIds.add(areaId);
if (ObjectUtil.isNotEmpty(areaIds)) {*/
query.where(tbDeviceInfo.deptAreaId.in(user.getDeptAreaId()));
// }
} else {
if(ObjectUtil.isNotEmpty(LoginHelper.getUserType()) && !LoginHelper.getUserType().equals(UserType.APP_USER)) {
//没有绑定区域查不到设备
query.where(tbDeviceInfo.id.eq("0"));
}
}
}
}
}else{
query.where(tbDeviceInfo.deptAreaId.isNull());
}
}
if (StringUtils.isNotBlank(subUid)) {
query.join(tbDeviceSubUser).on(tbDeviceSubUser.deviceId.eq(tbDeviceInfo.deviceId));
query.where(tbDeviceSubUser.uid.eq(subUid));
}
if (StringUtils.isNotBlank(productKey)) {
query.where(tbDeviceInfo.productKey.eq(productKey));
}
if(ObjectUtil.isNotNull(deviceStatus)){
query.where(tbDeviceInfo.deviceStatus.eq(deviceStatus));
}
if (online != null) {
query.where(tbDeviceInfo.state.eq(online ? "online" : "offline"));
}
if (StringUtils.isNotBlank(keyword)) {
query.where(tbDeviceInfo.deviceId.like("%" + keyword + "%")
.or(tbDeviceInfo.deviceName.like("%" + keyword + "%")));
}
if (ObjectUtil.isNotNull(startTime)&& ObjectUtil.isNotNull(endTime)) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date startDateTime = sdf.parse(startTime);
Date dateTime = sdf.parse(endTime);
// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// LocalDateTime startDateTime = LocalDateTime.parse(dictData.getStartTime() , formatter);
// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// LocalDateTime dateTime = LocalDateTime.parse(dictData.getEndTime(), formatter);
query.where(tbDeviceInfo.createTime.between(startDateTime, dateTime));
} catch (Exception e) {
}
}
query.orderBy(tbDeviceInfo.createAt.desc());
query.offset((page - 1) * size).limit(size);
List<TbDeviceInfo> tbDeviceInfos = query.fetch();
long total = query.fetchCount();
List<DeviceInfo> deviceInfos = new ArrayList<>(tbDeviceInfos.size());
for (TbDeviceInfo tbDeviceInfo : tbDeviceInfos) {
DeviceInfo deviceInfo = MapstructUtils.convert(tbDeviceInfo, DeviceInfo.class);
fillDeviceInfo(tbDeviceInfo.getDeviceId(), tbDeviceInfo, deviceInfo);
deviceInfos.add(deviceInfo);
}
return new Paging<>(total, deviceInfos);
}
public Paging<DeviceInfo> findByConditions(String name, String uid, String subUid,
String productKey, String groupId,
Boolean online, String keyword,

@ -77,4 +77,10 @@ public class DeviceInstallInfoDataImpl implements IDeviceInstallInfoData, IJPACo
public DeviceInstallInfo findByDeviceNameAndUidAndState(String deviceName, String uid, Integer state) {
return MapstructUtils.convert(homeRepository.findByDeviceNameAndUidAndState(deviceName,uid,state), DeviceInstallInfo.class);
}
@Override
public DeviceInstallInfo findById(String id) {
TbDeviceInstallInfo tbDeviceInstallInfo = homeRepository.findById(id).orElse(null);
return MapstructUtils.convert(tbDeviceInstallInfo, DeviceInstallInfo.class);
}
}

@ -222,6 +222,15 @@ public class SysRoleDataImpl implements ISysRoleData, IJPACommData<SysRole, Long
.fetch();
}
@Override
public List<SysRole> selectByTenantId(Long tenantId) {
return jpaQueryFactory.select(Projections.bean(SysRole.class, tbSysRole.id, tbSysRole.roleName, tbSysRole.roleKey, tbSysRole.roleSort, tbSysRole.dataScope, tbSysRole.status, tbSysRole.delFlag, tbSysRole.createTime, tbSysRole.remark))
.from(tbSysRole)
.where(PredicateBuilder.instance()
.and(tbSysRole.tenantId.eq(tenantId))
.build()).fetch();
}
private List<SysRole> buildQueryTitle(Predicate predicate) {
return jpaQueryFactory.selectDistinct(tbSysRole.id)
.select(Projections.fields(SysRole.class, tbSysRole.id, tbSysRole.roleName,

@ -13,13 +13,34 @@ import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class PDFGenerator {
// 缓存字体实例以避免重复加载
private static volatile PDType0Font cachedFont = null;
private static final Object fontLock = new Object();
// 缓存已处理的图片以避免重复下载和处理
private static final ConcurrentHashMap<String, PDImageXObject> imageCache = new ConcurrentHashMap<>();
// 线程池用于并发处理图片
private static final ExecutorService imageProcessingPool = Executors.newFixedThreadPool(4);
// 1. 表格参数配置
// 参数配置(优化后)
@ -32,12 +53,14 @@ public class PDFGenerator {
private static final float CELL_PADDING = 5;
private static final float[] COLUMN_WIDTHS = {LEFT_COL_WIDTH, RIGHT_COL_WIDTH};
private static final float MIN_IMAGE_HEIGHT = 50;
public static void generateInstallationPDF(DeviceInstallInfo entity, OutputStream outputStream) throws IOException {
public static void generateInstallationPDFBytes(DeviceInstallInfo entity, ByteArrayOutputStream outputStream) throws IOException {
try (PDDocument document = new PDDocument()) {
// 加载字体(需替换实际路径)
//PDType0Font font = PDType0Font.load(document, new File("D:\\NotoSansCJK-Regular.ttf"));
//PDType0Font font = PDType0Font.load(document, new File("/ttf/simsun.ttf"));
// 加载字体
// PDType0Font font = PDType0Font.load(document, new File("D:\\NotoSansCJK-Regular.ttf"));
PDType0Font font = PDType0Font.load(document, new File("/ttf/NotoSansCJK-Regular.ttf"));
// 初始化第一页
PDPage currentPage = new PDPage(PDRectangle.A4);
document.addPage(currentPage);
@ -46,20 +69,18 @@ public class PDFGenerator {
try {
// 1. 绘制标题(居中)
drawCenteredText(cs, font, FONT_SIZE+10,
drawCenteredText(cs, font, FONT_SIZE + 10,
"安装信息录入表",
PDRectangle.A4.getWidth()/2, currentY);
PDRectangle.A4.getWidth() / 2, currentY);
currentY -= ROW_HEIGHT * 1.5f;
// 2. 绘制基本信息表格
String[][] baseData = {
// {"公司名称", entity.getCorporateName()},
{"申请时间", entity.getProposerTime().toString()},
{"申请人", entity.getProposer()},
{"申请人班组", entity.getProposerTeam()},
{"小区名字", entity.getCommunityName()},
{"用户姓名", entity.getUserName()},
// {"用户姓名", entity.getUserName()+"-" + entity.getCommunityName()+entity.getBuildingUnit()+entity.getRoomNo()},
{"电话", entity.getUserIpone()},
{"楼栋单元号", entity.getBuildingUnit()},
{"房间号", entity.getRoomNo()},
@ -67,24 +88,24 @@ public class PDFGenerator {
{"切断阀编号", entity.getShutValueNumber()},
{"燃气表号", entity.getGasMeterNumber()},
{"备注", entity.getBuildingUnit()}
};
drawTable(cs, font, TABLE_MARGIN, currentY, baseData, COLUMN_WIDTHS);
currentY -= (ROW_HEIGHT * baseData.length) + 20f;
// @ApiModelProperty(value = "安装前图片")
// 创建后续页面 - 图片部分
// 图片处理(带自动分页)
// 图片处理(带自动分页)
// 3. 图片处理(带自动分页)
List<String[]> imageGroups = Arrays.asList(
new String[]{entity.getDeviceInfoImage(), "设备信息图片"},
new String[]{entity.getBeforeInstallationImage(), "安装前图片"},
new String[]{entity.getWorkingOfTheDetectorImage(), "安装完成探测器工作图片"},
new String[]{entity.getSideLeakageImage(), "测漏图片"},
new String[]{entity.getFormSideLeakageImage(), "泡沫水测漏图片"},
new String[]{entity.getIgnitionPictureImage(), "点火图片"},
new String[]{entity.getInstallThePanoramicImage(), "安装完成全景图片"},
new String[]{entity.getOfGasMeterImage(), "燃气表图片"},
new String[]{entity.getOfGasMeterImage(), "燃气表图片"},
new String[]{entity.getWorkOrderImage(), "工单图片"},
new String[]{entity.getPunchingImage(), "打孔图片"},
new String[]{entity.getFiexImage(), "安装电源线图片"}
new String[]{entity.getFiexImage(), "安装电源线图片"},
new String[]{entity.getHouseNumberImage(), "门牌号图片"}
);
for (String[] group : imageGroups) {
@ -98,7 +119,40 @@ public class PDFGenerator {
.collect(Collectors.toList());
if (!validUrls.isEmpty()) {
// float totalHeight = calculateTotalHeight(validUrls);
// 处理可能需要跨页的多图片情况
if (validUrls.size() > 2) {
// 对于多于2张的图片逐个或分组处理以支持跨页
for (int i = 0; i < validUrls.size(); i += 2) { // 每次处理最多2张图片
List<String> subList = validUrls.subList(i,
Math.min(i + 2, validUrls.size()));
List<String> wrappedTitleLines = wrapText(title,LEFT_COL_WIDTH - 2 * CELL_PADDING);
float titleHeight = wrappedTitleLines.size() * LINE_HEIGHT;
// 计算图片组的自适应高度
float imagesHeight = calculateAdjustedImagesHeight(document, subList,
RIGHT_COL_WIDTH - 2 * CELL_PADDING,
PDRectangle.A4.getHeight() - 2 * TABLE_MARGIN - titleHeight);
float totalHeight = Math.max(imagesHeight, titleHeight);
if (currentY < TABLE_MARGIN + Math.max(totalHeight, titleHeight)) {
cs.close();
currentPage = new PDPage(PDRectangle.A4);
document.addPage(currentPage);
cs = new PDPageContentStream(document, currentPage);
currentY = PDRectangle.A4.getHeight() - TABLE_MARGIN;
}
// 添加标识,表明这是多图片的一部分
String subTitle = title + " (" + (i+1) + "-" + Math.min(i+2, validUrls.size()) + "/" + validUrls.size() + ")";
List<String> subWrappedTitleLines = wrapText(subTitle, LEFT_COL_WIDTH - 2 * CELL_PADDING);
drawImageGroup(cs, document, font, subWrappedTitleLines, subList, currentY,
Math.max(totalHeight, titleHeight));
currentY -= Math.max(totalHeight, titleHeight);
}
} else {
// 原有的单/双图片处理逻辑
List<String> wrappedTitleLines = wrapText(title,LEFT_COL_WIDTH - 2 * CELL_PADDING);
float titleHeight = wrappedTitleLines.size() * LINE_HEIGHT;
// 计算图片组的自适应高度
@ -122,24 +176,263 @@ public class PDFGenerator {
}
}
}
}
} finally {
cs.close();
}
// document.save("D:/output.pdf");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
outputStream.flush();
baos.writeTo(outputStream);
// document.save(outputStream);
// 将PDF保存到输出流
document.save(outputStream);
}
}
public static void generateInstallationPDF(DeviceInstallInfo entity, OutputStream outputStream) throws IOException {
ByteArrayOutputStream pdfBaos = new ByteArrayOutputStream();
generateInstallationPDFBytes(entity, pdfBaos);
// 创建ZIP压缩包
try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {
zipOut.setLevel(Deflater.BEST_COMPRESSION);
ZipEntry entry = new ZipEntry(entity.getUserName() + entity.getCommunityName() + entity.getBuildingUnit() + entity.getRoomNo() + ".pdf");
zipOut.putNextEntry(entry);
byte[] pdfBytes = pdfBaos.toByteArray();
zipOut.write(pdfBytes, 0, pdfBytes.length);
zipOut.closeEntry();
}
}
// 辅助方法:生成文件名
public static String generateFileName(DeviceInstallInfo entity) {
return entity.getUserName() + entity.getCommunityName() +
entity.getBuildingUnit() + entity.getRoomNo() + ".pdf";
}
// public static void generateInstallationPDF(DeviceInstallInfo entity, OutputStream outputStream) throws IOException {
//
// // 首先生成PDF内容到字节数组
// ByteArrayOutputStream pdfBaos = new ByteArrayOutputStream();
//
// try (PDDocument document = new PDDocument()) {
// // 加载字体(需替换实际路径)
//// PDType0Font font = PDType0Font.load(document, new File("D:\\NotoSansCJK-Regular.ttf"));
// //PDType0Font font = PDType0Font.load(document, new File("/ttf/simsun.ttf"));
// PDType0Font font = PDType0Font.load(document, new File("/ttf/NotoSansCJK-Regular.ttf"));
// // 初始化第一页
// PDPage currentPage = new PDPage(PDRectangle.A4);
// document.addPage(currentPage);
// PDPageContentStream cs = new PDPageContentStream(document, currentPage);
// float currentY = PDRectangle.A4.getHeight() - TABLE_MARGIN;
//
// try {
// // 1. 绘制标题(居中)
// drawCenteredText(cs, font, FONT_SIZE+10,
// "安装信息录入表",
// PDRectangle.A4.getWidth()/2, currentY);
// currentY -= ROW_HEIGHT * 1.5f;
//
// // 2. 绘制基本信息表格
// String[][] baseData = {
// // {"公司名称", entity.getCorporateName()},
// {"申请时间", entity.getProposerTime().toString()},
// {"申请人", entity.getProposer()},
// {"申请人班组", entity.getProposerTeam()},
// {"小区名字", entity.getCommunityName()},
// {"用户姓名", entity.getUserName()},
// // {"用户姓名", entity.getUserName()+"-" + entity.getCommunityName()+entity.getBuildingUnit()+entity.getRoomNo()},
// {"电话", entity.getUserIpone()},
// {"楼栋单元号", entity.getBuildingUnit()},
// {"房间号", entity.getRoomNo()},
// {"报警器编号", entity.getDeviceName()},
// {"切断阀编号", entity.getShutValueNumber()},
// {"燃气表号", entity.getGasMeterNumber()},
// {"备注", entity.getBuildingUnit()}
//
// };
// drawTable(cs, font, TABLE_MARGIN, currentY, baseData, COLUMN_WIDTHS);
// currentY -= (ROW_HEIGHT * baseData.length) + 20f;
//// @ApiModelProperty(value = "安装前图片")
// // 创建后续页面 - 图片部分
// // 图片处理(带自动分页)
// // 图片处理(带自动分页)
// List<String[]> imageGroups = Arrays.asList(
// new String[]{entity.getDeviceInfoImage(), "设备信息图片"},
// new String[]{entity.getBeforeInstallationImage(), "安装前图片"},
// new String[]{entity.getWorkingOfTheDetectorImage(), "安装完成探测器工作图片"},
// new String[]{entity.getSideLeakageImage(), "测漏图片"},
// new String[]{entity.getFormSideLeakageImage(), "泡沫水测漏图片"},
// new String[]{entity.getIgnitionPictureImage(), "点火图片"},
// new String[]{entity.getInstallThePanoramicImage(), "安装完成全景图片"},
// new String[]{entity.getOfGasMeterImage(), "燃气表号图片"},
// new String[]{entity.getWorkOrderImage(), "工单图片"},
// new String[]{entity.getPunchingImage(), "打孔图片"},
// new String[]{entity.getFiexImage(), "安装电源线图片"},
// new String[]{entity.getHouseNumberImage(), "门牌号图片"}
// );
//
// for (String[] group : imageGroups) {
// String urls = group[0];
// String title = group[1];
//
// if (urls != null && !urls.trim().isEmpty()) {
// List<String> validUrls = Arrays.stream(urls.split(","))
// .map(String::trim)
// .filter(url -> !url.isEmpty() && url.startsWith("http"))
// .collect(Collectors.toList());
//
// if (!validUrls.isEmpty()) {
// // 处理可能需要跨页的多图片情况
// if (validUrls.size() > 2) {
// // 对于多于2张的图片逐个或分组处理以支持跨页
// for (int i = 0; i < validUrls.size(); i += 2) { // 每次处理最多2张图片
// List<String> subList = validUrls.subList(i,
// Math.min(i + 2, validUrls.size()));
//
// List<String> wrappedTitleLines = wrapText(title,LEFT_COL_WIDTH - 2 * CELL_PADDING);
// float titleHeight = wrappedTitleLines.size() * LINE_HEIGHT;
// // 计算图片组的自适应高度
// float imagesHeight = calculateAdjustedImagesHeight(document, subList,
// RIGHT_COL_WIDTH - 2 * CELL_PADDING,
// PDRectangle.A4.getHeight() - 2 * TABLE_MARGIN - titleHeight);
//
// float totalHeight = Math.max(imagesHeight, titleHeight);
//
// if (currentY < TABLE_MARGIN + Math.max(totalHeight, titleHeight)) {
// cs.close();
// currentPage = new PDPage(PDRectangle.A4);
// document.addPage(currentPage);
// cs = new PDPageContentStream(document, currentPage);
// currentY = PDRectangle.A4.getHeight() - TABLE_MARGIN;
// }
//
// // 添加标识,表明这是多图片的一部分
// String subTitle = title + " (" + (i+1) + "-" + Math.min(i+2, validUrls.size()) + "/" + validUrls.size() + ")";
// List<String> subWrappedTitleLines = wrapText(subTitle, LEFT_COL_WIDTH - 2 * CELL_PADDING);
//
// drawImageGroup(cs, document, font, subWrappedTitleLines, subList, currentY,
// Math.max(totalHeight, titleHeight));
// currentY -= Math.max(totalHeight, titleHeight);
// }
// } else {
// // 原有的单/双图片处理逻辑
// List<String> wrappedTitleLines = wrapText(title,LEFT_COL_WIDTH - 2 * CELL_PADDING);
// float titleHeight = wrappedTitleLines.size() * LINE_HEIGHT;
// // 计算图片组的自适应高度
// float imagesHeight = calculateAdjustedImagesHeight(document, validUrls,
// RIGHT_COL_WIDTH - 2 * CELL_PADDING,
// PDRectangle.A4.getHeight() - 2 * TABLE_MARGIN - titleHeight);
//
// float totalHeight = Math.max(imagesHeight, titleHeight);
//
// if (currentY < TABLE_MARGIN + Math.max(totalHeight, titleHeight)) {
// cs.close();
// currentPage = new PDPage(PDRectangle.A4);
// document.addPage(currentPage);
// cs = new PDPageContentStream(document, currentPage);
// currentY = PDRectangle.A4.getHeight() - TABLE_MARGIN;
// }
//
// drawImageGroup(cs, document, font, wrappedTitleLines, validUrls, currentY,
// Math.max(totalHeight, titleHeight));
// currentY -= Math.max(totalHeight, titleHeight);
// }
// }
// }
// }
////
// } finally {
// cs.close();
// }
// // 将PDF保存到ByteArrayOutputStream
// document.save(pdfBaos);
//
//// ByteArrayOutputStream baos = new ByteArrayOutputStream();
//// document.save(baos);
//// outputStream.flush();
//// baos.writeTo(outputStream);
//
//
// }
// // 创建ZIP压缩包并将PDF写入输出流
// try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {
// // 设置最高压缩级别
// zipOut.setLevel(Deflater.BEST_COMPRESSION);
// // 创建ZIP条目
// ZipEntry entry = new ZipEntry(entity.getUserName()+entity.getCommunityName()+entity.getBuildingUnit()+entity.getRoomNo()+".pdf");
// zipOut.putNextEntry(entry);
//
// // 将PDF数据写入ZIP
// byte[] pdfBytes = pdfBaos.toByteArray();
// zipOut.write(pdfBytes, 0, pdfBytes.length);
//
// // 关闭当前条目
// zipOut.closeEntry();
// }
// }
// 加载字体,确保使用正确的文档实例
private static PDType0Font loadFont(PDDocument document) throws IOException {
// 尝试多个可能的字体路径
String[] fontPaths = {
"D:\\NotoSansCJK-Regular.ttf",
"C:\\Windows\\Fonts\\simsun.ttc",
"C:\\Windows\\Fonts\\msyh.ttc",
"/System/Library/Fonts/Helvetica.dfont", // macOS
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" // Linux
};
for (String path : fontPaths) {
if (Files.exists(Paths.get(path))) {
try {
return PDType0Font.load(document, new File(path));
} catch (IOException e) {
continue; // 尝试下一个字体文件
}
}
}
return PDType0Font.load(document, new File("/ttf/NotoSansCJK-Regular.ttf"));
}
// 缓存已处理的图片以避免重复下载和处理
private static final ConcurrentHashMap<String, byte[]> imageByteCache = new ConcurrentHashMap<>();
// 预加载图片以避免重复下载
private static PDImageXObject loadImageFromUrl(PDDocument document, String url) throws IOException {
byte[] imageBytes = imageByteCache.computeIfAbsent(url, key -> {
try {
return new URL(key).openStream().readAllBytes();
} catch (IOException e) {
return null; // 或者抛出异常
}
});
if (imageBytes == null) {
return null;
}
return PDImageXObject.createFromByteArray(document, imageBytes, "");
}
private static List<String> wrapText(String text, float maxWidth) {
List<String> lines = new ArrayList<>();
int maxCharsPerLine = (int)(maxWidth / (FONT_SIZE * 0.5f)); // 中文字符估算
if (text == null || text.isEmpty()) return lines;
// 优化的文本换行逻辑,减少计算量
int maxCharsPerLine = (int) (maxWidth / (FONT_SIZE * 0.4f)); // 调整字符估算系数
if (text.length() <= maxCharsPerLine) {
lines.add(text);
return lines;
}
StringBuilder currentLine = new StringBuilder();
for (String word : text.split("")) {
String[] words = text.split("");
for (String word : words) {
if (currentLine.length() + word.length() <= maxCharsPerLine) {
currentLine.append(word);
} else {
@ -152,6 +445,24 @@ public class PDFGenerator {
}
return lines;
}
// private static List<String> wrapText(String text, float maxWidth) {
// List<String> lines = new ArrayList<>();
// int maxCharsPerLine = (int)(maxWidth / (FONT_SIZE * 0.5f)); // 中文字符估算
//
// StringBuilder currentLine = new StringBuilder();
// for (String word : text.split("")) {
// if (currentLine.length() + word.length() <= maxCharsPerLine) {
// currentLine.append(word);
// } else {
// lines.add(currentLine.toString());
// currentLine = new StringBuilder(word);
// }
// }
// if (currentLine.length() > 0) {
// lines.add(currentLine.toString());
// }
// return lines;
// }
private static float calculateTotalHeight(List<String> imageUrls) throws IOException {
@ -261,7 +572,7 @@ public class PDFGenerator {
// 单图片模式:一页两行,每行占半页高度
float singleImageHeight = imageAvailableHeight;
// 绘制标题(垂直居中)
float titleY = startY - totalHeight/2 + (titleLines.size() * LINE_HEIGHT)/2;
float titleY = startY - totalHeight / 2 + (titleLines.size() * LINE_HEIGHT) / 2;
// 绘制第一行标题(顶部)
// float titleY = startY - CELL_PADDING - FONT_SIZE;
cs.beginText();
@ -275,8 +586,8 @@ public class PDFGenerator {
cs.endText();
// 绘制第一行图片(自动缩放)
PDImageXObject image1 = PDImageXObject.createFromByteArray(doc,
new URL(imageUrls.get(0)).openStream().readAllBytes(), "embedded-image");
PDImageXObject image1 = loadImageFromUrl(doc, imageUrls.get(0));
if (image1 != null) {
float scale1 = Math.min(
imageAvailableWidth / image1.getWidth(),
singleImageHeight / image1.getHeight()
@ -288,13 +599,14 @@ public class PDFGenerator {
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
startY - CELL_PADDING - scaledHeight1,
scaledWidth1, scaledHeight1);
}
} else if (imageUrls.size() == 2) {
// 双图片模式:改为上下布局
float imageHeightPer = (imageAvailableHeight - CELL_PADDING) / 2;
// 绘制标题(垂直居中)
float titleY = startY - totalHeight/2 + (titleLines.size() * LINE_HEIGHT)/2;
float titleY = startY - totalHeight / 2 + (titleLines.size() * LINE_HEIGHT) / 2;
cs.beginText();
cs.setFont(font, FONT_SIZE);
cs.newLineAtOffset(TABLE_MARGIN + CELL_PADDING, titleY);
@ -306,23 +618,25 @@ public class PDFGenerator {
cs.endText();
// 绘制第一张图片(上)
PDImageXObject image1 = PDImageXObject.createFromByteArray(doc,
new URL(imageUrls.get(0)).openStream().readAllBytes(), "embedded-image");
PDImageXObject image1 = loadImageFromUrl(doc, imageUrls.get(0));
float scaledHeight1 = 0;
if (image1 != null) {
float scale1 = Math.min(
imageAvailableWidth / image1.getWidth(),
imageHeightPer / image1.getHeight()
);
float scaledHeight1 = image1.getHeight() * scale1;
scaledHeight1 = image1.getHeight() * scale1;
float scaledWidth1 = image1.getWidth() * scale1;
cs.drawImage(image1,
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
startY - CELL_PADDING - scaledHeight1,
scaledWidth1, scaledHeight1);
}
// 绘制第二张图片(下)
PDImageXObject image2 = PDImageXObject.createFromByteArray(doc,
new URL(imageUrls.get(1)).openStream().readAllBytes(), "embedded-image");
PDImageXObject image2 = loadImageFromUrl(doc, imageUrls.get(1));
if (image2 != null) {
float scale2 = Math.min(
imageAvailableWidth / image2.getWidth(),
imageHeightPer / image2.getHeight()
@ -334,6 +648,7 @@ public class PDFGenerator {
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
startY - CELL_PADDING - scaledHeight1 - CELL_PADDING - scaledHeight2,
scaledWidth2, scaledHeight2);
}
/* // 双图片模式:一行并列显示
float imageWidthPer = (imageAvailableWidth - CELL_PADDING) / 2;
@ -378,8 +693,239 @@ public class PDFGenerator {
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING + imageWidthPer + CELL_PADDING,
startY - CELL_PADDING - scaledHeight2,
scaledWidth2, scaledHeight2);*/
} else {
// 多图片模式超过2张图片垂直堆叠显示与双图片模式保持一致的样式
float imageHeightPer = (imageAvailableHeight - CELL_PADDING * (imageUrls.size() - 1)) / imageUrls.size();
// 绘制标题(垂直居中)
float titleY = startY - totalHeight / 2 + (titleLines.size() * LINE_HEIGHT) / 2;
cs.beginText();
cs.setFont(font, FONT_SIZE);
cs.newLineAtOffset(TABLE_MARGIN + CELL_PADDING, titleY);
for (String line : titleLines) {
cs.showText(line);
titleY -= LINE_HEIGHT;
cs.newLineAtOffset(0, -LINE_HEIGHT);
}
cs.endText();
// 绘制所有图片,采用与单/双图片一致的样式
float currentY = startY - CELL_PADDING;
for (int i = 0; i < imageUrls.size(); i++) {
PDImageXObject image = loadImageFromUrl(doc, imageUrls.get(i));
if (image == null) continue; // 跳过加载失败的图片
float scale = Math.min(
imageAvailableWidth / image.getWidth(),
imageHeightPer / image.getHeight()
);
float scaledHeight = image.getHeight() * scale;
float scaledWidth = image.getWidth() * scale;
// 检查是否需要跨页
if (currentY - scaledHeight - CELL_PADDING < TABLE_MARGIN) {
// 当前页面空间不足,需要跨页
// 这里需要在实际应用中处理跨页逻辑
// 由于在单个drawImageGroup方法中无法直接创建新页面
// 需要在调用此方法的循环中处理跨页
break; // 暂停绘制剩余图片,由外部循环处理跨页
}
// 绘制当前图片
cs.drawImage(image,
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
currentY - scaledHeight,
scaledWidth, scaledHeight);
// 更新下一个图片的起始Y坐标
currentY -= (scaledHeight + CELL_PADDING);
}
}
}
// private static void drawImageGroup(PDPageContentStream cs, PDDocument doc, PDType0Font font,
// List<String> titleLines, List<String> imageUrls,
// float startY, float totalHeight) throws IOException {
// // 绘制单元格边框
// cs.setLineWidth(0.5f);
// cs.addRect(TABLE_MARGIN, startY - totalHeight, LEFT_COL_WIDTH, totalHeight);
// cs.addRect(TABLE_MARGIN + LEFT_COL_WIDTH, startY - totalHeight, RIGHT_COL_WIDTH, totalHeight);
// cs.stroke();
//
// // 计算每张图片的可用高度(根据图片数量动态调整)
// float imageAvailableHeight = totalHeight - 2 * CELL_PADDING;
// float imageAvailableWidth = RIGHT_COL_WIDTH - 2 * CELL_PADDING;
//
// if (imageUrls.size() == 1) {
// // 单图片模式:一页两行,每行占半页高度
// float singleImageHeight = imageAvailableHeight;
// // 绘制标题(垂直居中)
// float titleY = startY - totalHeight/2 + (titleLines.size() * LINE_HEIGHT)/2;
// // 绘制第一行标题(顶部)
// // float titleY = startY - CELL_PADDING - FONT_SIZE;
// cs.beginText();
// cs.setFont(font, FONT_SIZE);
// cs.newLineAtOffset(TABLE_MARGIN + CELL_PADDING, titleY);
// for (String line : titleLines) {
// cs.showText(line);
// titleY -= LINE_HEIGHT;
// cs.newLineAtOffset(0, -LINE_HEIGHT);
// }
// cs.endText();
//
// // 绘制第一行图片(自动缩放)
// PDImageXObject image1 = PDImageXObject.createFromByteArray(doc,
// new URL(imageUrls.get(0)).openStream().readAllBytes(), "embedded-image");
// float scale1 = Math.min(
// imageAvailableWidth / image1.getWidth(),
// singleImageHeight / image1.getHeight()
// );
// float scaledHeight1 = image1.getHeight() * scale1;
// float scaledWidth1 = image1.getWidth() * scale1;
//
// cs.drawImage(image1,
// TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
// startY - CELL_PADDING - scaledHeight1,
// scaledWidth1, scaledHeight1);
//
// } else if (imageUrls.size() == 2) {
// // 双图片模式:改为上下布局
// float imageHeightPer = (imageAvailableHeight - CELL_PADDING) / 2;
//
// // 绘制标题(垂直居中)
// float titleY = startY - totalHeight/2 + (titleLines.size() * LINE_HEIGHT)/2;
// cs.beginText();
// cs.setFont(font, FONT_SIZE);
// cs.newLineAtOffset(TABLE_MARGIN + CELL_PADDING, titleY);
// for (String line : titleLines) {
// cs.showText(line);
// titleY -= LINE_HEIGHT;
// cs.newLineAtOffset(0, -LINE_HEIGHT);
// }
// cs.endText();
//
// // 绘制第一张图片(上)
// PDImageXObject image1 = PDImageXObject.createFromByteArray(doc,
// new URL(imageUrls.get(0)).openStream().readAllBytes(), "embedded-image");
// float scale1 = Math.min(
// imageAvailableWidth / image1.getWidth(),
// imageHeightPer / image1.getHeight()
// );
// float scaledHeight1 = image1.getHeight() * scale1;
// float scaledWidth1 = image1.getWidth() * scale1;
//
// cs.drawImage(image1,
// TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
// startY - CELL_PADDING - scaledHeight1,
// scaledWidth1, scaledHeight1);
//
// // 绘制第二张图片(下)
// PDImageXObject image2 = PDImageXObject.createFromByteArray(doc,
// new URL(imageUrls.get(1)).openStream().readAllBytes(), "embedded-image");
// float scale2 = Math.min(
// imageAvailableWidth / image2.getWidth(),
// imageHeightPer / image2.getHeight()
// );
// float scaledHeight2 = image2.getHeight() * scale2;
// float scaledWidth2 = image2.getWidth() * scale2;
//
// cs.drawImage(image2,
// TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
// startY - CELL_PADDING - scaledHeight1 - CELL_PADDING - scaledHeight2,
// scaledWidth2, scaledHeight2);
// /* // 双图片模式:一行并列显示
// float imageWidthPer = (imageAvailableWidth - CELL_PADDING) / 2;
//
// // 绘制标题(垂直居中)
// float titleY = startY - totalHeight/2 + (titleLines.size() * LINE_HEIGHT)/2;
// cs.beginText();
// cs.setFont(font, FONT_SIZE);
// cs.newLineAtOffset(TABLE_MARGIN + CELL_PADDING, titleY);
// for (String line : titleLines) {
// cs.showText(line);
// titleY -= LINE_HEIGHT;
// cs.newLineAtOffset(0, -LINE_HEIGHT);
// }
// cs.endText();
//
// // 绘制第一张图片(左)
// PDImageXObject image1 = PDImageXObject.createFromByteArray(doc,
// new URL(imageUrls.get(0)).openStream().readAllBytes(), "embedded-image");
// float scale1 = Math.min(
// imageWidthPer / image1.getWidth(),
// imageAvailableHeight / image1.getHeight()
// );
// float scaledHeight1 = image1.getHeight() * scale1;
// float scaledWidth1 = image1.getWidth() * scale1;
//
// cs.drawImage(image1,
// TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
// startY - CELL_PADDING - scaledHeight1,
// scaledWidth1, scaledHeight1);
//
// // 绘制第二张图片(右)
// PDImageXObject image2 = PDImageXObject.createFromByteArray(doc,
// new URL(imageUrls.get(1)).openStream().readAllBytes(), "embedded-image");
// float scale2 = Math.min(
// imageWidthPer / image2.getWidth(),
// imageAvailableHeight / image2.getHeight()
// );
// float scaledHeight2 = image2.getHeight() * scale2;
// float scaledWidth2 = image2.getWidth() * scale2;
//
// cs.drawImage(image2,
// TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING + imageWidthPer + CELL_PADDING,
// startY - CELL_PADDING - scaledHeight2,
// scaledWidth2, scaledHeight2);*/
// }else {
// // 多图片模式超过2张图片垂直堆叠显示与双图片模式保持一致的样式
// float imageHeightPer = (imageAvailableHeight - CELL_PADDING * (imageUrls.size() - 1)) / imageUrls.size();
//
// // 绘制标题(垂直居中)
// float titleY = startY - totalHeight / 2 + (titleLines.size() * LINE_HEIGHT) / 2;
// cs.beginText();
// cs.setFont(font, FONT_SIZE);
// cs.newLineAtOffset(TABLE_MARGIN + CELL_PADDING, titleY);
// for (String line : titleLines) {
// cs.showText(line);
// titleY -= LINE_HEIGHT;
// cs.newLineAtOffset(0, -LINE_HEIGHT);
// }
// cs.endText();
//
// // 绘制所有图片,采用与单/双图片一致的样式
// float currentY = startY - CELL_PADDING;
// for (int i = 0; i < imageUrls.size(); i++) {
// PDImageXObject image = PDImageXObject.createFromByteArray(doc,
// new URL(imageUrls.get(i)).openStream().readAllBytes(), "embedded-image");
// float scale = Math.min(
// imageAvailableWidth / image.getWidth(),
// imageHeightPer / image.getHeight()
// );
// float scaledHeight = image.getHeight() * scale;
// float scaledWidth = image.getWidth() * scale;
//
// // 检查是否需要跨页
// if (currentY - scaledHeight - CELL_PADDING < TABLE_MARGIN) {
// // 当前页面空间不足,需要跨页
// // 这里需要在实际应用中处理跨页逻辑
// // 由于在单个drawImageGroup方法中无法直接创建新页面
// // 需要在调用此方法的循环中处理跨页
// break; // 暂停绘制剩余图片,由外部循环处理跨页
// }
//
// // 绘制当前图片
// cs.drawImage(image,
// TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
// currentY - scaledHeight,
// scaledWidth, scaledHeight);
//
// // 更新下一个图片的起始Y坐标
// currentY -= (scaledHeight + CELL_PADDING);
// }
// }
// }
private static float calculateAdjustedImagesHeight(PDDocument doc, List<String> imageUrls,
float maxWidth, float maxHeight) throws IOException {
if (imageUrls.isEmpty()) return 0;
@ -388,14 +934,28 @@ public class PDFGenerator {
if (imageUrls.size() == 1) {
// 单图片模式:占用半页高度
return maxHeight / 2;
} else {
} else if (imageUrls.size() == 2) {
// 双图片模式:整页高度
return maxHeight;
} else {
// 多图片模式:根据图片数量动态计算高度
// 每张图片预留最小高度,防止页面过长
float minImageHeight = 50f; // 每张图片最小高度
float imageSpacing = 5f; // 图片间距
float totalSpacing = (imageUrls.size() - 1) * imageSpacing;
float estimatedHeight = (minImageHeight * imageUrls.size()) + totalSpacing;
// 如果估算高度超过页面可用高度,则返回最大高度,需要分页处理
if (estimatedHeight > maxHeight) {
return maxHeight;
}
return estimatedHeight;
}
}
// 截断超长文本
private static String truncateText(PDType0Font font,String text, float maxWidth) throws IOException {
private static String truncateText(PDType0Font font, String text, float maxWidth) throws IOException {
// PDType0Font font = ...; // 获取字体对象
StringBuilder sb = new StringBuilder();
float currentWidth = 0;
@ -421,8 +981,8 @@ public class PDFGenerator {
}
// 简单估算:假设每个中文字符宽度等于字体大小
int charsPerLine = (int)(maxWidth / fontSize);
int lineCount = (int)Math.ceil((double)text.length() / charsPerLine);
int charsPerLine = (int) (maxWidth / fontSize);
int lineCount = (int) Math.ceil((double) text.length() / charsPerLine);
return lineCount * fontSize * 1.2f; // 行间距1.2倍
}
@ -435,7 +995,7 @@ public class PDFGenerator {
cs.setLeading(fontSize * 1.2f); // 行间距
// 简单换行实现
int charsPerLine = (int)(maxWidth / (fontSize * 0.8)); // 中文字符估算
int charsPerLine = (int) (maxWidth / (fontSize * 0.8)); // 中文字符估算
int pos = 0;
float currentY = startY;
@ -452,6 +1012,7 @@ public class PDFGenerator {
currentY -= fontSize * 1.2f;
}
}
// 图片高度预估方法
private static float estimateImageHeight(String imageUrl, float targetWidth) throws IOException {
try (InputStream is = new URL(imageUrl).openStream()) {
@ -589,8 +1150,6 @@ public class PDFGenerator {
}*/
private static void drawTable(PDPageContentStream cs, PDType0Font font,
float x, float y, String[][] data, float[] colWidths) throws IOException {
@ -615,7 +1174,7 @@ public class PDFGenerator {
// 绘制列分隔线
float currentX = x;
for (int i = 1; i < colWidths.length; i++) {
currentX += colWidths[i-1];
currentX += colWidths[i - 1];
cs.moveTo(currentX, y);
cs.lineTo(currentX, y - ROW_HEIGHT * data.length);
cs.stroke();
@ -652,13 +1211,13 @@ public class PDFGenerator {
}
private static float addImageRow(PDPageContentStream cs, PDDocument doc,
PDType0Font font, String title, String imageUrl,
float startX, float startY) throws IOException {
// 加载图片
PDImageXObject image = PDImageXObject.createFromByteArray(doc,
new URL(imageUrl).openStream().readAllBytes(), "embedded-image");
PDImageXObject image = loadImageFromUrl(doc, imageUrl);
if (image == null) return startY; // 如果图片加载失败,返回原位置
// 动态计算尺寸
float imageWidth = RIGHT_COL_WIDTH - 10;
float scale = imageWidth / image.getWidth();
@ -672,7 +1231,7 @@ public class PDFGenerator {
cs.stroke();
// 垂直居中文本(改进算法)
float textY = startY - rowHeight/2 + FONT_SIZE/2;
float textY = startY - rowHeight / 2 + FONT_SIZE / 2;
//String title = "安装完成探测器工作图片\n检测时间2025-08-14";
/* if(title.contains("\n")){
@ -680,7 +1239,7 @@ public class PDFGenerator {
textY = startY - rowHeight/2 + FONT_SIZE/2 + (lines.length-1)*FONT_SIZE/2;
}*/
drawCenteredText(cs, font, FONT_SIZE, title,
startX + LEFT_COL_WIDTH/2, textY);
startX + LEFT_COL_WIDTH / 2, textY);
// 绘制图片带5pt边距
cs.drawImage(image,
@ -725,6 +1284,79 @@ public class PDFGenerator {
return startY - dynamicRowHeight - 10;*/
}
// private static float addImageRow(PDPageContentStream cs, PDDocument doc,
// PDType0Font font, String title, String imageUrl,
// float startX, float startY) throws IOException {
// // 加载图片
// PDImageXObject image = PDImageXObject.createFromByteArray(doc,
// new URL(imageUrl).openStream().readAllBytes(), "embedded-image");
// // 动态计算尺寸
// float imageWidth = RIGHT_COL_WIDTH - 10;
// float scale = imageWidth / image.getWidth();
// float imageHeight = image.getHeight() * scale;
// float rowHeight = imageHeight + 20;
//
// // 绘制单元格边框
// cs.setLineWidth(0.5f);
// cs.addRect(startX, startY - rowHeight, LEFT_COL_WIDTH, rowHeight);
// cs.addRect(startX + LEFT_COL_WIDTH, startY - rowHeight, RIGHT_COL_WIDTH, rowHeight);
// cs.stroke();
//
// // 垂直居中文本(改进算法)
// float textY = startY - rowHeight/2 + FONT_SIZE/2;
// //String title = "安装完成探测器工作图片\n检测时间2025-08-14";
//
// /* if(title.contains("\n")){
// String[] lines = title.split("\n");
// textY = startY - rowHeight/2 + FONT_SIZE/2 + (lines.length-1)*FONT_SIZE/2;
// }*/
// drawCenteredText(cs, font, FONT_SIZE, title,
// startX + LEFT_COL_WIDTH/2, textY);
//
// // 绘制图片带5pt边距
// cs.drawImage(image,
// startX + LEFT_COL_WIDTH + 5,
// startY - rowHeight + 10,
// imageWidth, imageHeight);
//
// return startY - rowHeight - 10;
// /*// 计算图片尺寸(保持比例)
// float imageWidth = RIGHT_COL_WIDTH - 10;
// float scale = imageWidth / image.getWidth();
// float imageHeight = image.getHeight() * scale;
//
// // 动态计算行高(图片高度+上下边距20
// float dynamicRowHeight = imageHeight + 20;
//
// // 绘制左侧标题单元格
// cs.setLineWidth(0.5f);
// cs.addRect(TABLE_MARGIN, startY - dynamicRowHeight, LEFT_COL_WIDTH, dynamicRowHeight);
//
// // 绘制右侧图片单元格(高度与左侧同步)
// cs.addRect(TABLE_MARGIN + LEFT_COL_WIDTH, startY - dynamicRowHeight, RIGHT_COL_WIDTH, dynamicRowHeight);
// cs.stroke();
//
// // 居中绘制标题文本
// float titleWidth = font.getStringWidth(title) / 1000 * FONT_SIZE;
// float titleX = TABLE_MARGIN + (LEFT_COL_WIDTH - titleWidth) / 2;
// float titleY = startY - (dynamicRowHeight / 2) - (FONT_SIZE / 3);
//
// cs.beginText();
// cs.setFont(font, FONT_SIZE);
// cs.newLineAtOffset(titleX, titleY);
// cs.showText(title);
// cs.endText();
//
// // 居中绘制图片
// float imageX = TABLE_MARGIN + LEFT_COL_WIDTH + 5;
// float imageY = startY - dynamicRowHeight + 10;
// cs.drawImage(image, imageX, imageY, imageWidth, imageHeight);
//
// // 返回新的Y坐标当前位置减去行高和间距
// return startY - dynamicRowHeight - 10;*/
// }
/* private static float addImageRow(PDPageContentStream cs, PDDocument doc,
PDType0Font font, String title, String imageUrl, float x, float y) throws IOException {
// 绘制标题行

@ -37,6 +37,8 @@ import cc.iotkit.common.utils.MapstructUtils;
import cc.iotkit.data.manager.IDeviceInstallInfoData;
import cc.iotkit.manager.config.GasInstallationPDFGenerator;
import cc.iotkit.manager.config.PDFGenerator;
import cc.iotkit.manager.dto.bo.BatchPdfWrapper;
import cc.iotkit.manager.dto.bo.ExportTask;
import cc.iotkit.manager.dto.bo.device.DeviceDetectorQueryBo;
import cc.iotkit.manager.dto.bo.device.DeviceInstallInfoQueryBo;
import cc.iotkit.manager.dto.bo.device.DeviceQueryBo;
@ -100,6 +102,11 @@ import java.net.URLEncoder;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static cc.iotkit.data.model.QTbDeviceInstallInfo.tbDeviceInstallInfo;
import static com.itextpdf.kernel.pdf.PdfName.*;
@ -116,6 +123,12 @@ public class DeviceInstallInfoController {
@Autowired
private IDeviceInstallInfoData iDeviceInstallInfoData;
@Autowired
private ExecutorService taskExecutor;
// 存储任务状态
private static final ConcurrentHashMap<String, ExportTask> exportTasks = new ConcurrentHashMap<>();
@ApiOperation(value = "安装列表", notes = "设备列表", httpMethod = "POST")
//@SaCheckPermission("iot:device:query")
@PostMapping("/page")
@ -201,6 +214,57 @@ public class DeviceInstallInfoController {
PDFGenerator.generateInstallationPDF(data, response.getOutputStream());
}
@PostMapping("/getPdfBatch")
@SaCheckPermission("device:install:upload")
public void getPdfBatch(@Validated @RequestBody Request<BatchPdfWrapper> request, HttpServletResponse response) throws Exception {
List<String> ids = request.getData().getData();
if (ids == null || ids.isEmpty()) {
throw new BizException(ErrCode.PARAMS_EXCEPTION);
}
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"批量安装信息.zip\"");
// 创建ZIP输出流
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
zipOut.setLevel(Deflater.BEST_COMPRESSION);
for (String id : ids) {
DeviceInstallInfo data = iDeviceInstallInfoData.findById(id);
if (data == null) {
continue; // 跳过不存在的记录
}
// 生成PDF字节数组
ByteArrayOutputStream pdfBaos = new ByteArrayOutputStream();
PDFGenerator.generateInstallationPDFBytes(data, pdfBaos);
byte[] pdfBytes = pdfBaos.toByteArray();
// 创建ZIP条目
String fileName = data.getUserName() + "_" +
data.getCommunityName() + "_" +
data.getBuildingUnit() + data.getRoomNo() + ".pdf";
// 清理文件名中的非法字符
fileName = fileName.replaceAll("[\\\\/:*?\"<>|]", "_");
ZipEntry entry = new ZipEntry(fileName);
zipOut.putNextEntry(entry);
zipOut.write(pdfBytes, 0, pdfBytes.length);
zipOut.closeEntry();
}
zipOut.finish();
}
}
@ApiOperation("导出设备列表")
@Log(title = "导出设备列表", businessType = BusinessType.EXPORT)
// @SaCheckPermission("system:user:export")@RequestBody Request<ProductBo>

@ -0,0 +1,11 @@
package cc.iotkit.manager.dto.bo;
import lombok.Data;
import java.util.List;
@Data
public class BatchExportRequest {
private List<String> data;
private Long tenantId;
}

@ -0,0 +1,11 @@
package cc.iotkit.manager.dto.bo;
import lombok.Data;
import java.util.List;
@Data
public class BatchPdfWrapper {
private List<String> data;
private Long tenantId;
}

@ -0,0 +1,28 @@
package cc.iotkit.manager.dto.bo;
import lombok.Data;
import java.util.Date;
import java.util.List;
// 任务状态类
@Data
public class ExportTask {
private String taskId;
private List<String> ids;
private String status; // PENDING, PROCESSING, COMPLETED, FAILED
private String filePath;
private String errorMessage;
private Date createTime;
private Date completeTime;
private int progress; // 进度 0-100
public ExportTask(String taskId, List<String> ids, String status) {
this.taskId = taskId;
this.ids = ids;
this.status = status;
this.createTime = new Date();
this.progress = 0;
}
}

@ -90,5 +90,8 @@ public class DeviceQueryBo extends BaseDto {
private String endTime;
private String config;
//设备状态0正常1故障2报警3离线
private Integer deviceStatus;
}

@ -56,4 +56,7 @@ public class DeviceInfoExpordVo implements Serializable {
private Long tenantId;
@ExcelProperty(value = "设备区域")
private Long deptAreaId;
@ExcelProperty(value = "设备位置")
private String site;
}

@ -57,4 +57,6 @@ public class DeviceInfoImportVo implements Serializable {
private Long tenantId;
@ExcelProperty(value = "设备区域")
private Long deptAreaId;
@ExcelProperty(value = "设备位置")
private String site;
}

@ -61,6 +61,10 @@ public class DeviceInstallInfoExpordVo implements Serializable {
// @ApiModelProperty(value = "切断阀编号")
@ExcelProperty(value = "地址")
private String site;
@ExcelProperty(value = "切断阀编号")
private String shutValueNumber;
@ExcelProperty(value = "报警器编号")
private String deviceName;
// @ApiModelProperty(value = "燃气表号")
@ExcelProperty(value = "表号")
private String gasMeterNumber;

@ -64,6 +64,10 @@ public class DeviceInstallInfoVo implements Serializable {
// @ApiModelProperty(value = "切断阀编号")
@ExcelProperty(value = "地址")
private String site;
@ExcelProperty(value = "切断阀编号")
private String shutValueNumber;
@ExcelProperty(value = "报警器编号")
private String deviceName;
// @ApiModelProperty(value = "燃气表号")
@ExcelProperty(value = "表号")
private String gasMeterNumber;

@ -223,6 +223,8 @@ public class DeviceInstallInfoServiceImpl implements IDeviceInstallInfoService {
DeviceInstallInfo deviceInfo = MapstructUtils.convert(tbDeviceInfo, DeviceInstallInfo.class);
DeviceInstallInfoVo deviceInstallInfoVo = MapstructUtils.convert(deviceInfo, DeviceInstallInfoVo.class);
deviceInstallInfoVo.setXuhao(i);
deviceInstallInfoVo.setShutValueNumber(deviceInfo.getShutValueNumber());
deviceInstallInfoVo.setDeviceName(deviceInfo.getDeviceName());
deviceInstallInfoVo.setSite(tbDeviceInfo.getCommunityName() + tbDeviceInfo.getBuildingUnit() + tbDeviceInfo.getRoomNo());
deviceInstallInfoVo.setManufacturer("天津费加罗");
deviceInstallInfoVo.setPosition("成功");

@ -148,13 +148,14 @@ public class DeviceManagerServiceImpl implements IDeviceManagerService {
String name = query.getName();
String deviceName = query.getDeviceName();
String pk = query.getProductKey();
Integer deviceStatus = query.getDeviceStatus();
//关键字查询
String keyword = query.getKeyword();
String group = query.getGroup();
Boolean online = query.getOnline();
Long areaDepeId = query.getDeptAreaId();
Paging<DeviceInfoVo> result = MapstructUtils.convert(deviceInfoData.findByConditions(name, uid, subUid, pk, group,
online, keyword, pageRequest.getPageNum(), pageRequest.getPageSize(), areaDepeId, query.getStartTime(), query.getEndTime(), deviceName), DeviceInfoVo.class);
online, keyword, pageRequest.getPageNum(), pageRequest.getPageSize(), areaDepeId, query.getStartTime(), query.getEndTime(), deviceName,deviceStatus), DeviceInfoVo.class);
List<Category> categorys = categoryData.findAll();
for (DeviceInfoVo row : result.getRows()) {
if (ObjectUtil.isNotEmpty(row.getProductKey())) {

@ -62,6 +62,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.security.PermitAll;
import java.time.LocalDateTime;
@ -163,6 +164,7 @@ public class ThingServiceImpl implements IThingService {
switch (type) {
case REGISTER:
//设备注册
// 根据
String deviceId = registerDevice(device, (DeviceRegister) action, null);
deviceUpdateLastTime(deviceId, lastTime);
@ -367,8 +369,9 @@ public class ThingServiceImpl implements IThingService {
.deviceStatus(device.getDeviceStatus())
.occurred(System.currentTimeMillis())
.build();
//增加启用未启用条件查询
List<AlertConfig> config = iAlertConfigData.findByDeviceName(device.getDeviceName());
//在设备列表中,添加了手机号并且选择了通知方式、语音、短信等。其实是添加到了告警事件配置里面。
// List<AlertRecord> alerts = idAlertRecordData.selectAlertConfigPage(AlertRecordBo.builder().details(device.getDeviceName()).build());
System.out.println("设备告警 您的设备【" + device.getDeviceName() + "】触发事件 事件类型为:【" + map.get("eventTypeValue").toString() + "】。");
if (ObjectUtil.isEmpty(config)) {
@ -379,7 +382,12 @@ public class ThingServiceImpl implements IThingService {
executor.execute(() -> {
List<Rule> rules = ruleMessageHandler.processMessage1(thingModelMessage);
for (Rule rule : rules) {
List<AlertConfig> config1 = iAlertConfigData.findByRuleInfoId(rule.getId());
//这里也要加增加启用未启用条件查询
List<AlertConfig> config1 = iAlertConfigData.findByRuleInfoIdAndDeviceName(rule.getId(),device.getDeviceName());
if(CollectionUtils.isEmpty(config1)){
config1 = iAlertConfigData.findByRuleInfoIdAndDeviceName(rule.getId(),null);
}
for (int i = 0; i < config1.size(); i++) {
idAlertRecordData.save(AlertRecord.builder()
.level(config1.get(i).getLevel())
@ -394,28 +402,28 @@ public class ThingServiceImpl implements IThingService {
.deptAreaId(device.getDeptAreaId())
.readFlg(false)
.alertTime(System.currentTimeMillis())
.details("您的设备【" + device.getDeviceName() + "】触发事件 事件类型为:【" + map.get("eventTypeValue").toString() + "】。")
.build());
if (ObjectUtil.isNotEmpty(deviceInfoArrayList1)) {
for (int j = 0; j < deviceInfoArrayList1.size(); j++) {
String deviceName = deviceInfoArrayList1.get(j).getNodeStatus().equals("1")?device.getDeviceName(): deviceInfoArrayList1.get(j).getNodeDevice();
idAlertRecordData.save(AlertRecord.builder()
.level(config1.get(i).getLevel())
.name(config1.get(i).getName())
.reasonForRemovalFlag(0)
.productKey(device.getProductKey())
.levelStatus(eventTypeValue1)
.deviceName(deviceInfoArrayList1.get(j).getDeviceName())
.site(deviceInfoArrayList1.get(j).getSite())
.tenantId(deviceInfoArrayList1.get(j).getTenantId())
.uid(deviceInfoArrayList1.get(j).getUid())
.deptAreaId(deviceInfoArrayList1.get(j).getDeptAreaId())
.readFlg(false)
.alertTime(System.currentTimeMillis())
.details("您的设备【" + deviceInfoArrayList1.get(j).getDeviceName() + "】,节点" + deviceInfoArrayList1.get(j).getNode() + "触发事件,事件类型:【联动箱输出】," + "告警设备:" + deviceName)
.details("您的设备【" + device.getDeviceName() + "】,位置【" + device.getSite() + "】触发事件 事件类型为:【" + map.get("eventTypeValue").toString() + "】。")
.build());
}
}
// if (ObjectUtil.isNotEmpty(deviceInfoArrayList1)) {
// for (int j = 0; j < deviceInfoArrayList1.size(); j++) {
// String deviceName = deviceInfoArrayList1.get(j).getNodeStatus().equals("1")?device.getDeviceName(): deviceInfoArrayList1.get(j).getNodeDevice();
// idAlertRecordData.save(AlertRecord.builder()
// .level(config1.get(i).getLevel())
// .name(config1.get(i).getName())
// .reasonForRemovalFlag(0)
// .productKey(device.getProductKey())
// .levelStatus(eventTypeValue1)
// .deviceName(deviceInfoArrayList1.get(j).getDeviceName())
// .site(deviceInfoArrayList1.get(j).getSite())
// .tenantId(deviceInfoArrayList1.get(j).getTenantId())
// .uid(deviceInfoArrayList1.get(j).getUid())
// .deptAreaId(deviceInfoArrayList1.get(j).getDeptAreaId())
// .readFlg(false)
// .alertTime(System.currentTimeMillis())
// .details("您的设备【" + deviceInfoArrayList1.get(j).getDeviceName() + "】,节点" + deviceInfoArrayList1.get(j).getNode() + "触发事件,事件类型:【联动箱输出】," + "告警设备:" + deviceName)
// .build());
// }
// }
ruleExecutor.execute1(thingModelMessage, config1);
}
}
@ -443,28 +451,28 @@ public class ThingServiceImpl implements IThingService {
.deptAreaId(device.getDeptAreaId())
.readFlg(false)
.alertTime(System.currentTimeMillis())
.details("您的设备【" + device.getDeviceName() + "】触发事件 事件类型为:【" + map.get("eventTypeValue").toString() + "】。")
.build());
if (ObjectUtil.isNotEmpty(deviceInfoArrayList1)) {
for (int j = 0; j < deviceInfoArrayList1.size(); j++) {
String deviceName = deviceInfoArrayList1.get(j).getNodeStatus().equals("1")?device.getDeviceName(): deviceInfoArrayList1.get(j).getNodeDevice();
idAlertRecordData.save(AlertRecord.builder()
.level(config.get(i).getLevel())
.name(config.get(i).getName())
.site(deviceInfoArrayList1.get(j).getSite())
.reasonForRemovalFlag(0)
.productKey(device.getProductKey())
.levelStatus(eventTypeValue1)
.deviceName(deviceInfoArrayList1.get(j).getDeviceName())
.tenantId(deviceInfoArrayList1.get(j).getTenantId())
.uid(deviceInfoArrayList1.get(j).getUid())
.deptAreaId(deviceInfoArrayList1.get(j).getDeptAreaId())
.readFlg(false)
.alertTime(System.currentTimeMillis())
.details("您的设备【" + deviceInfoArrayList1.get(j).getDeviceName() + "】,节点" + deviceInfoArrayList1.get(j).getNode() + "触发事件,事件类型:【联动箱输出】," + "告警设备:" + deviceName)
.details("您的设备【" + device.getDeviceName() + "】,位置【" + device.getSite() + "】触发事件 事件类型为:【" + map.get("eventTypeValue").toString() + "】。")
.build());
}
}
// if (ObjectUtil.isNotEmpty(deviceInfoArrayList1)) {
// for (int j = 0; j < deviceInfoArrayList1.size(); j++) {
// String deviceName = deviceInfoArrayList1.get(j).getNodeStatus().equals("1")?device.getDeviceName(): deviceInfoArrayList1.get(j).getNodeDevice();
// idAlertRecordData.save(AlertRecord.builder()
// .level(config.get(i).getLevel())
// .name(config.get(i).getName())
// .site(deviceInfoArrayList1.get(j).getSite())
// .reasonForRemovalFlag(0)
// .productKey(device.getProductKey())
// .levelStatus(eventTypeValue1)
// .deviceName(deviceInfoArrayList1.get(j).getDeviceName())
// .tenantId(deviceInfoArrayList1.get(j).getTenantId())
// .uid(deviceInfoArrayList1.get(j).getUid())
// .deptAreaId(deviceInfoArrayList1.get(j).getDeptAreaId())
// .readFlg(false)
// .alertTime(System.currentTimeMillis())
// .details("您的设备【" + deviceInfoArrayList1.get(j).getDeviceName() + "】,节点" + deviceInfoArrayList1.get(j).getNode() + "触发事件,事件类型:【联动箱输出】," + "告警设备:" + deviceName)
// .build());
// }
// }
ruleExecutor.execute1(thingModelMessage, config);
}
// RuleExecutor ruleExecutor = new RuleExecutor();
@ -491,7 +499,21 @@ public class ThingServiceImpl implements IThingService {
flag = false;
if (deviceInfo2.getNodeOneStatus().equals("1")) {
//选中的任意一个设备报警就发送联动控制指令
String[] deviceNames = {
deviceInfo2.getNodeOneDevice(),
deviceInfo2.getNodeTwoDevice(),
deviceInfo2.getNodeThreeDevice(),
deviceInfo2.getNodeFourDevice(),
deviceInfo2.getNodeFiveDevice()
};
boolean matchFound = Arrays.stream(deviceNames)
.filter(Objects::nonNull)
.anyMatch(name -> name.equals(device.getDeviceName()));
if (matchFound) {
flag = deviceLinkageControlSend(deviceInfo2, deviceInfo2.getNodeOne());
}
} else {
//满足所有的设备告警才触发联动控制
String[] nodeOneDevice = deviceInfo2.getNodeOneDevice().split(",");
@ -513,7 +535,21 @@ public class ThingServiceImpl implements IThingService {
flag = false;
if (deviceInfo2.getNodeTwoStatus().equals("1")) {
//选中的任意一个设备报警就发送联动控制指令
flag = deviceLinkageControlSend(deviceInfo2, deviceInfo2.getNodeTwo());
String[] deviceNames = {
deviceInfo2.getNodeOneDevice(),
deviceInfo2.getNodeTwoDevice(),
deviceInfo2.getNodeThreeDevice(),
deviceInfo2.getNodeFourDevice(),
deviceInfo2.getNodeFiveDevice()
};
boolean matchFound = Arrays.stream(deviceNames)
.filter(Objects::nonNull)
.anyMatch(name -> name.equals(device.getDeviceName()));
if (matchFound) {
flag = deviceLinkageControlSend(deviceInfo2, deviceInfo2.getNodeOne());
}
} else {
//满足所有的设备告警才触发联动控制
String[] nodeOneDevice = deviceInfo2.getNodeTwoDevice().split(",");
@ -532,7 +568,21 @@ public class ThingServiceImpl implements IThingService {
flag = false;
if (deviceInfo2.getNodeThreeStatus().equals("1")) {
//选中的任意一个设备报警就发送联动控制指令
flag = deviceLinkageControlSend(deviceInfo2, deviceInfo2.getNodeThree());
String[] deviceNames = {
deviceInfo2.getNodeOneDevice(),
deviceInfo2.getNodeTwoDevice(),
deviceInfo2.getNodeThreeDevice(),
deviceInfo2.getNodeFourDevice(),
deviceInfo2.getNodeFiveDevice()
};
boolean matchFound = Arrays.stream(deviceNames)
.filter(Objects::nonNull)
.anyMatch(name -> name.equals(device.getDeviceName()));
if (matchFound) {
flag = deviceLinkageControlSend(deviceInfo2, deviceInfo2.getNodeOne());
}
} else {
//满足所有的设备告警才触发联动控制
String[] nodeOneDevice = deviceInfo2.getNodeThreeDevice().split(",");
@ -551,7 +601,21 @@ public class ThingServiceImpl implements IThingService {
flag = false;
if (deviceInfo2.getNodeFourStatus().equals("1")) {
//选中的任意一个设备报警就发送联动控制指令
flag = deviceLinkageControlSend(deviceInfo2, deviceInfo2.getNodeFour());
String[] deviceNames = {
deviceInfo2.getNodeOneDevice(),
deviceInfo2.getNodeTwoDevice(),
deviceInfo2.getNodeThreeDevice(),
deviceInfo2.getNodeFourDevice(),
deviceInfo2.getNodeFiveDevice()
};
boolean matchFound = Arrays.stream(deviceNames)
.filter(Objects::nonNull)
.anyMatch(name -> name.equals(device.getDeviceName()));
if (matchFound) {
flag = deviceLinkageControlSend(deviceInfo2, deviceInfo2.getNodeOne());
}
} else {
//满足所有的设备告警才触发联动控制
String[] nodeOneDevice = deviceInfo2.getNodeFourDevice().split(",");
@ -570,7 +634,21 @@ public class ThingServiceImpl implements IThingService {
flag = false;
if (deviceInfo2.getNodeFiveStatus().equals("1")) {
//选中的任意一个设备报警就发送联动控制指令
flag = deviceLinkageControlSend(deviceInfo2, deviceInfo2.getNodeFive());
String[] deviceNames = {
deviceInfo2.getNodeOneDevice(),
deviceInfo2.getNodeTwoDevice(),
deviceInfo2.getNodeThreeDevice(),
deviceInfo2.getNodeFourDevice(),
deviceInfo2.getNodeFiveDevice()
};
boolean matchFound = Arrays.stream(deviceNames)
.filter(Objects::nonNull)
.anyMatch(name -> name.equals(device.getDeviceName()));
if (matchFound) {
flag = deviceLinkageControlSend(deviceInfo2, deviceInfo2.getNodeOne());
}
} else {
//满足所有的设备告警才触发联动控制
String[] nodeOneDevice = deviceInfo2.getNodeFiveDevice().split(",");
@ -667,12 +745,27 @@ public class ThingServiceImpl implements IThingService {
"controlOutput", node + "0001"
))
.build();
idAlertRecordData.save(AlertRecord.builder()
.level("1")
.name("联动控制箱下发")
.reasonForRemovalFlag(0)
.productKey(device.getProductKey())
.levelStatus("联动控制箱下发")
.deviceName(device.getDeviceName())
.site(device.getSite())
.tenantId(device.getTenantId())
.uid(device.getUid())
.deptAreaId(device.getDeptAreaId())
.readFlg(false)
.alertTime(System.currentTimeMillis())
.details("您的设备【" + device.getDeviceName() + "】,节点" + node + "触发事件,事件类型:【联动箱输出】。")
.build());
//联动控制箱下发指令
deviceService.invoke(thingService);
return true;
}
//浓度变化
//浓度变化,事件类型:
public void extractDetectorData(DeviceInfo device, Map<String, Object> baseInfo) {
for (Map.Entry<String, Object> entry : baseInfo.entrySet()) {
if (entry.getKey().startsWith("dataDetectorValue")) {
@ -887,7 +980,7 @@ public class ThingServiceImpl implements IThingService {
if (productKey != null && device.getProductKey() != null
&& !device.getProductKey().equals(productKey)) {
//防止设备变更了产品key
device.setProductKey(productKey);
// device.setProductKey(productKey);
// device.setModel(register.getModel());
deviceInfoData.save(device);
}

@ -28,10 +28,12 @@ import cc.iotkit.common.thing.ThingModelMessage;
import cc.iotkit.common.utils.JsonUtils;
import cc.iotkit.data.manager.IAlertConfigData;
import cc.iotkit.data.manager.IDeviceInfoData;
import cc.iotkit.data.manager.IProductData;
import cc.iotkit.data.manager.IUserInfoData;
import cc.iotkit.model.UserInfo;
import cc.iotkit.model.alert.AlertConfig;
import cc.iotkit.model.device.DeviceInfo;
import cc.iotkit.model.product.Product;
import cc.iotkit.model.rule.RuleLog;
import cc.iotkit.model.worder.WorderDO;
import cc.iotkit.ruleengine.action.Action;
@ -63,9 +65,11 @@ import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.provider.enumerate.SupplierType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import javax.security.auth.message.config.AuthConfig;
@ -100,6 +104,14 @@ public class RuleExecutor {
@Resource
private WxMaService wxMaService;
@Autowired
@Qualifier("productDataCache")
private IProductData productData;
@Autowired
@Qualifier("deviceInfoDataCache")
private IDeviceInfoData deviceInfoData;
public void execute1(ThingModelMessage message,List<AlertConfig> listAlertConfigs) {
DeviceInfo deviceInfo = iDeviceInfoData.findByDeviceId(message.getDeviceId());
// 注入 WxMaService 后直接调用
@ -279,6 +291,7 @@ public class RuleExecutor {
case "0":
//发送短信
try {
Date date = new Date(message.getTime());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String templateId = "SMS_492470379";
@ -303,6 +316,23 @@ public class RuleExecutor {
System.out.println("短信接口返回的数据:" + smsResponse);
/* if (!"OK".equals(smsResponse.getCode())) {
}*/
// 发送联动控制箱短信
List<DeviceInfo> deviceInfos = deviceLinkageControl(deviceInfo);
if(CollectionUtils.isEmpty(deviceInfos)){
break;
}
String templateId1 = "SMS_496730336";
LinkedHashMap<String, String> map1 = new LinkedHashMap<>(1);
map.put("boxDeviceName", deviceInfos.get(0).getDeviceName());
String deviceName = deviceInfos.get(0).getNodeStatus().equals("1")?deviceInfo.getDeviceName(): deviceInfos.get(0).getNodeDevice();
String node = deviceInfos.get(0).getNode();
map.put("node", node);
map.put("deviceSite", site);
map.put("deviceName", deviceName);
SmsBlend smsBlend1 = SmsFactory.createSmsBlend(SupplierType.ALIBABA);
SmsResponse smsResponse1 = smsBlend1.sendMessage(userInfo.getUserName(), templateId1, map);
System.out.println("短信接口返回的数据:" + smsResponse1);
} catch (Exception e) {
e.printStackTrace();
}
@ -518,4 +548,98 @@ public class RuleExecutor {
return results;
}
private List<DeviceInfo> deviceLinkageControl(DeviceInfo device) {
List<DeviceInfo> deviceInfo1 = new ArrayList<>();
deviceInfo1 = getDeviceInfo(device.getDeptAreaId(), "MQFejp7cyDMH3enG");
Boolean flag = false;
String nodeStatus = null;
List<DeviceInfo> deviceInfo55 = new ArrayList<>();
if (deviceInfo1 != null && !deviceInfo1.isEmpty()) {
for (DeviceInfo deviceInfo2 : deviceInfo1) {
if (ObjectUtil.isNotNull(deviceInfo2.getNodeOne()) && ObjectUtil.isNotNull(deviceInfo2.getNodeOneStatus()) && ObjectUtil.isNotNull(deviceInfo2.getNodeOneDevice())) {
if(device.getDeviceName().equals(deviceInfo2.getNodeOneDevice())){
nodeStatus = "1";
deviceInfo2.setNode(nodeStatus);
deviceInfo2.setNodeStatus(deviceInfo2.getNodeOneStatus());
deviceInfo2.setNodeDevice(deviceInfo2.getNodeOneDevice());
deviceInfo55.add(deviceInfo2);
}
}
if (ObjectUtil.isNotNull(deviceInfo2.getNodeTwo()) && ObjectUtil.isNotNull(deviceInfo2.getNodeTwoStatus()) && ObjectUtil.isNotNull(deviceInfo2.getNodeTwoDevice())) {
//deviceLinkageControlSend(deviceInfo2,deviceInfo2.getNodeTwo());
if(device.getDeviceName().equals(deviceInfo2.getNodeTwoDevice())){
nodeStatus = "2";
deviceInfo2.setNode(nodeStatus);
deviceInfo2.setNodeStatus(deviceInfo2.getNodeTwoStatus());
deviceInfo2.setNodeDevice(deviceInfo2.getNodeTwoDevice());
deviceInfo55.add(deviceInfo2);
}
}
if (ObjectUtil.isNotNull(deviceInfo2.getNodeThree()) && ObjectUtil.isNotNull(deviceInfo2.getNodeThreeStatus()) && ObjectUtil.isNotNull(deviceInfo2.getNodeThreeDevice())) {
// deviceLinkageControlSend(deviceInfo2,deviceInfo2.getNodeThree());
if(device.getDeviceName().equals(deviceInfo2.getNodeThreeDevice())){
nodeStatus = "3";
deviceInfo2.setNode(nodeStatus);
deviceInfo2.setNodeStatus(deviceInfo2.getNodeThreeStatus());
deviceInfo2.setNodeDevice(deviceInfo2.getNodeThreeDevice());
deviceInfo55.add(deviceInfo2);
}
}
if (ObjectUtil.isNotNull(deviceInfo2.getNodeFour()) && ObjectUtil.isNotNull(deviceInfo2.getNodeFourStatus()) && ObjectUtil.isNotNull(deviceInfo2.getNodeFourDevice())) {
// deviceLinkageControlSend(deviceInfo2,deviceInfo2.getNodeFour());
if(device.getDeviceName().equals(deviceInfo2.getNodeFourDevice())){
nodeStatus = "4";
deviceInfo2.setNode(nodeStatus);
deviceInfo2.setNodeStatus(deviceInfo2.getNodeFourStatus());
deviceInfo2.setNodeDevice(deviceInfo2.getNodeFourDevice());
deviceInfo55.add(deviceInfo2);
}
}
if (ObjectUtil.isNotNull(deviceInfo2.getNodeFive()) && ObjectUtil.isNotNull(deviceInfo2.getNodeFiveStatus()) && ObjectUtil.isNotNull(deviceInfo2.getNodeFiveDevice())) {
// deviceLinkageControlSend(deviceInfo2,deviceInfo2.getNodeFive());
if(device.getDeviceName().equals(deviceInfo2.getNodeFiveDevice())){
nodeStatus = "5";
deviceInfo2.setNode(nodeStatus);
deviceInfo2.setNodeStatus(deviceInfo2.getNodeFiveStatus());
deviceInfo2.setNodeDevice(deviceInfo2.getNodeFiveDevice());
deviceInfo55.add(deviceInfo2);
}
}
}
}
return deviceInfo55;
}
public List<DeviceInfo> getDeviceInfo(Long areaDeptId, String productKey) {
try {
return deviceInfoData.findByDeptAreaIdAndProductKey(areaDeptId, productKey);
// return deviceInfoData.findByDeptAreaIdAndProductKeyAndDeviceName(areaDeptId, productKey, deviceName);
} catch (Throwable e) {
log.error("get device error", e);
return null;
}
}
}

@ -362,7 +362,8 @@ public class SysTenantServiceImpl implements ISysTenantService {
//删除角色
SysRole querySysRole=new SysRole();
querySysRole.setTenantId(tenantId);
List<SysRole> roles =sysRoleData.selectRoleList(querySysRole);
// List<SysRole> roles =sysRoleData.selectRoleList(querySysRole);
List<SysRole> roles =sysRoleData.selectByTenantId(tenantId);
sysRoleData.deleteByIds(roles.stream().map(SysRole::getId).collect(Collectors.toList()));
//删除部门
List<SysDept> lsDepts = sysDeptData.findByTenantId(tenantId);

@ -28,6 +28,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Configuration
@EnableScheduling
public class SchedulerConfig {
@ -41,4 +44,10 @@ public class SchedulerConfig {
return scheduler;
}
@Bean
public ExecutorService taskExecutor() {
// 创建一个固定大小的线程池大小为10
return Executors.newFixedThreadPool(10);
}
}

@ -52,6 +52,7 @@ spring:
hibernate:
format_sql: false
use_sql_comments: false # 输出SQL注释
generate_statistics: false # 关闭统计信息
session_factory:
# statement_inspector: com.example.TenantSQLInterceptor
# dialect: org.hibernate.dialect.MySQL5Dialect
@ -59,7 +60,8 @@ spring:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
url: jdbc:mysql://123.57.78.108:3306/hma_iot?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
url: jdbc:mysql://172.16.0.64:3306/hma_iot?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
# url: jdbc:mysql://123.57.78.108:3306/hma_iot_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 1093e4769496a6ed
#password: CGKhbMfrdz6w3icX
@ -81,7 +83,8 @@ spring:
rest:
#使用内置es的配置
#uris: http://elasticsearch:9200
uris: http://123.57.78.108:9301
# uris: http://123.57.78.108:9301
uris: http://172.16.0.64:9301
username: elastic
password: hmkj@2023
connection-timeout: 10s
@ -109,7 +112,8 @@ spring:
embedded:
enabled: false
#host: redis
host: 123.57.78.108
# host: 123.57.78.108
host: 172.16.0.64
port: 6379
database: 11
password: HMkj@2023

@ -72,6 +72,12 @@
<appender-ref ref="console"/>
</logger>
<logger name="org.hibernate.SQL" level="OFF"/>
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="OFF"/>
<logger name="org.hibernate.type.descriptor.sql" level="OFF"/>
<logger name="org.hibernate.engine.QueryParameters" level="OFF"/>
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="OFF"/>
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="trace"/>
<!-- <logger name="org.springframework" level="debug" additivity="false">-->

Loading…
Cancel
Save