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

@ -38,4 +38,6 @@ public interface IAlertConfigData extends ICommonData<AlertConfig, Long> {
List<AlertConfig> findByUidAndRuleInfoIdAndDeviceName(String uid, String ruleInfoId,String deviceName); List<AlertConfig> findByUidAndRuleInfoIdAndDeviceName(String uid, String ruleInfoId,String deviceName);
Paging<AlertConfig> selectAlertConfigPage(PageRequest<AlertConfig> request); 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 page
* @param size * @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, Paging<DeviceInfo> findByConditions(String name,String uid, String subUid, String productKey,
String groupId, Boolean online, String keyword, String groupId, Boolean online, String keyword,
int page, int size,Long areaDepeId,String startTime,String endTime,String deviceName); 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(); List<DeviceInfo> findNeverUsedDevices();
Long findNeverUsedDevices(DeviceInfo deviceInfo,Boolean flag); Long findNeverUsedDevices(DeviceInfo deviceInfo,Boolean flag);
List<AlertStatDTO> getDeviceRecord(); 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 findByDeviceNameAndUid(String deviceName,String uid);
DeviceInstallInfo findByDeviceNameAndUidAndState(String deviceName,String uid,Integer state); 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> selectRoleList(SysRole role);
List<SysRole> findByUserId(Long id); List<SysRole> findByUserId(Long id);
List<SysRole> selectByTenantId(Long tenantId);
} }

@ -127,6 +127,12 @@ public class DeviceInfoDataCache implements IDeviceInfoData, SmartInitializingSi
public List<AlertStatDTO> getDeviceRecord() { public List<AlertStatDTO> getDeviceRecord() {
return deviceInfoData.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) { 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); 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 @Override
public Paging<DeviceInfo> findByConditionsExcel(String name,String uid, String subUid, String productKey, 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) { 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() { public List<AlertStatDTO> getDeviceRecord() {
return deviceInfoData.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 @Override
public List<DeviceInfo> findByParentId(String parentId) { public List<DeviceInfo> findByParentId(String parentId) {
return deviceInfoData.findByParentId(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) { 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); 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 @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) { 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); 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> findByRuleInfoIdAndEnable(String ruleInfoId,Boolean enable);
List<TbAlertConfig> findByDeviceNameAndEnable(String deviceName,Boolean enable); List<TbAlertConfig> findByDeviceNameAndEnable(String deviceName,Boolean enable);
List<TbAlertConfig> findByUidAndRuleInfoIdAndDeviceNameAndEnable(String uid, String ruleInfoId,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); List<TbDeviceInfo> findByDeptAreaIdAndProductKey(Long deptAreaId,String productKey);
long countByUid(String uid); long countByUid(String uid);
long countByUidAndState(String uid,String state); 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; private String workingOfTheDetectorImage;
@ApiModelProperty(value = "测漏图片") @ApiModelProperty(value = "测漏图片")
private String sideLeakageImage; private String sideLeakageImage;
@ApiModelProperty(value = "泡沫水测漏图片")
private String formSideLeakageImage;
@ApiModelProperty(value = "点火图片") @ApiModelProperty(value = "点火图片")
private String ignitionPictureImage; private String ignitionPictureImage;
@ApiModelProperty(value = "装完成全景图片") @ApiModelProperty(value = "装完成全景图片")
@ -98,6 +100,10 @@ public class TbDeviceInstallInfo extends BaseEntity implements TenantAware {
private String punchingImage; private String punchingImage;
@ApiModelProperty(value = "装电源线照片") @ApiModelProperty(value = "装电源线照片")
private String fiexImage; private String fiexImage;
@ApiModelProperty(value = "设备信息图片")
private String deviceInfoImage;
@ApiModelProperty(value = "门牌号图片")
private String houseNumberImage;
@ApiModelProperty(value = "用户id") @ApiModelProperty(value = "用户id")
private String uid; private String uid;

@ -82,6 +82,12 @@ public class AlertConfigDataImpl implements IAlertConfigData, IJPACommData<Alert
.findAll(buildQueryCondition(request.getData()), PageBuilder.toPageable(request)); .findAll(buildQueryCondition(request.getData()), PageBuilder.toPageable(request));
return new Paging<>(alertConfigPage.getTotalElements(), MapstructUtils.convert(alertConfigPage.getContent(), AlertConfig.class)); 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 @Override
public List<AlertConfig> findByUidAndRuleInfoId(String uid, String ruleInfoId) { public List<AlertConfig> findByUidAndRuleInfoId(String uid, String ruleInfoId) {
return MapstructUtils.convert(alertConfigRepository.findByUidAndRuleInfoId(uid, ruleInfoId), AlertConfig.class); 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 @Override
public Paging<DeviceInfo> findByConditionsExcel(String name, String uid, String subUid, public Paging<DeviceInfo> findByConditionsExcel(String name, String uid, String subUid,
String productKey, String groupId, String productKey, String groupId,
@ -629,6 +634,143 @@ public class DeviceInfoDataImpl implements IDeviceInfoData, IJPACommData<DeviceI
return new Paging<>(total, deviceInfos); return new Paging<>(total, deviceInfos);
} }
@Override @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, public Paging<DeviceInfo> findByConditions(String name, String uid, String subUid,
String productKey, String groupId, String productKey, String groupId,
Boolean online, String keyword, Boolean online, String keyword,

@ -77,4 +77,10 @@ public class DeviceInstallInfoDataImpl implements IDeviceInstallInfoData, IJPACo
public DeviceInstallInfo findByDeviceNameAndUidAndState(String deviceName, String uid, Integer state) { public DeviceInstallInfo findByDeviceNameAndUidAndState(String deviceName, String uid, Integer state) {
return MapstructUtils.convert(homeRepository.findByDeviceNameAndUidAndState(deviceName,uid,state), DeviceInstallInfo.class); 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(); .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) { private List<SysRole> buildQueryTitle(Predicate predicate) {
return jpaQueryFactory.selectDistinct(tbSysRole.id) return jpaQueryFactory.selectDistinct(tbSysRole.id)
.select(Projections.fields(SysRole.class, tbSysRole.id, tbSysRole.roleName, .select(Projections.fields(SysRole.class, tbSysRole.id, tbSysRole.roleName,

@ -13,13 +13,34 @@ import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
import java.net.URL; import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; 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.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 { 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. 表格参数配置 // 1. 表格参数配置
// 参数配置(优化后) // 参数配置(优化后)
@ -32,12 +53,14 @@ public class PDFGenerator {
private static final float CELL_PADDING = 5; private static final float CELL_PADDING = 5;
private static final float[] COLUMN_WIDTHS = {LEFT_COL_WIDTH, RIGHT_COL_WIDTH}; private static final float[] COLUMN_WIDTHS = {LEFT_COL_WIDTH, RIGHT_COL_WIDTH};
private static final float MIN_IMAGE_HEIGHT = 50; 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()) { try (PDDocument document = new PDDocument()) {
// 加载字体(需替换实际路径) // 加载字体
//PDType0Font font = PDType0Font.load(document, new File("D:\\NotoSansCJK-Regular.ttf")); // 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")); PDType0Font font = PDType0Font.load(document, new File("/ttf/NotoSansCJK-Regular.ttf"));
// 初始化第一页 // 初始化第一页
PDPage currentPage = new PDPage(PDRectangle.A4); PDPage currentPage = new PDPage(PDRectangle.A4);
document.addPage(currentPage); document.addPage(currentPage);
@ -46,20 +69,18 @@ public class PDFGenerator {
try { try {
// 1. 绘制标题(居中) // 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; currentY -= ROW_HEIGHT * 1.5f;
// 2. 绘制基本信息表格 // 2. 绘制基本信息表格
String[][] baseData = { String[][] baseData = {
// {"公司名称", entity.getCorporateName()},
{"申请时间", entity.getProposerTime().toString()}, {"申请时间", entity.getProposerTime().toString()},
{"申请人", entity.getProposer()}, {"申请人", entity.getProposer()},
{"申请人班组", entity.getProposerTeam()}, {"申请人班组", entity.getProposerTeam()},
{"小区名字", entity.getCommunityName()}, {"小区名字", entity.getCommunityName()},
{"用户姓名", entity.getUserName()}, {"用户姓名", entity.getUserName()},
// {"用户姓名", entity.getUserName()+"-" + entity.getCommunityName()+entity.getBuildingUnit()+entity.getRoomNo()},
{"电话", entity.getUserIpone()}, {"电话", entity.getUserIpone()},
{"楼栋单元号", entity.getBuildingUnit()}, {"楼栋单元号", entity.getBuildingUnit()},
{"房间号", entity.getRoomNo()}, {"房间号", entity.getRoomNo()},
@ -67,24 +88,24 @@ public class PDFGenerator {
{"切断阀编号", entity.getShutValueNumber()}, {"切断阀编号", entity.getShutValueNumber()},
{"燃气表号", entity.getGasMeterNumber()}, {"燃气表号", entity.getGasMeterNumber()},
{"备注", entity.getBuildingUnit()} {"备注", entity.getBuildingUnit()}
}; };
drawTable(cs, font, TABLE_MARGIN, currentY, baseData, COLUMN_WIDTHS); drawTable(cs, font, TABLE_MARGIN, currentY, baseData, COLUMN_WIDTHS);
currentY -= (ROW_HEIGHT * baseData.length) + 20f; currentY -= (ROW_HEIGHT * baseData.length) + 20f;
// @ApiModelProperty(value = "安装前图片")
// 创建后续页面 - 图片部分 // 3. 图片处理(带自动分页)
// 图片处理(带自动分页)
// 图片处理(带自动分页)
List<String[]> imageGroups = Arrays.asList( List<String[]> imageGroups = Arrays.asList(
new String[]{entity.getDeviceInfoImage(), "设备信息图片"},
new String[]{entity.getBeforeInstallationImage(), "安装前图片"}, new String[]{entity.getBeforeInstallationImage(), "安装前图片"},
new String[]{entity.getWorkingOfTheDetectorImage(), "安装完成探测器工作图片"}, new String[]{entity.getWorkingOfTheDetectorImage(), "安装完成探测器工作图片"},
new String[]{entity.getSideLeakageImage(), "测漏图片"}, new String[]{entity.getSideLeakageImage(), "测漏图片"},
new String[]{entity.getFormSideLeakageImage(), "泡沫水测漏图片"},
new String[]{entity.getIgnitionPictureImage(), "点火图片"}, new String[]{entity.getIgnitionPictureImage(), "点火图片"},
new String[]{entity.getInstallThePanoramicImage(), "安装完成全景图片"}, new String[]{entity.getInstallThePanoramicImage(), "安装完成全景图片"},
new String[]{entity.getOfGasMeterImage(), "燃气表图片"}, new String[]{entity.getOfGasMeterImage(), "燃气表图片"},
new String[]{entity.getWorkOrderImage(), "工单图片"}, new String[]{entity.getWorkOrderImage(), "工单图片"},
new String[]{entity.getPunchingImage(), "打孔图片"}, new String[]{entity.getPunchingImage(), "打孔图片"},
new String[]{entity.getFiexImage(), "安装电源线图片"} new String[]{entity.getFiexImage(), "安装电源线图片"},
new String[]{entity.getHouseNumberImage(), "门牌号图片"}
); );
for (String[] group : imageGroups) { for (String[] group : imageGroups) {
@ -98,7 +119,40 @@ public class PDFGenerator {
.collect(Collectors.toList()); .collect(Collectors.toList());
if (!validUrls.isEmpty()) { 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); List<String> wrappedTitleLines = wrapText(title,LEFT_COL_WIDTH - 2 * CELL_PADDING);
float titleHeight = wrappedTitleLines.size() * LINE_HEIGHT; float titleHeight = wrappedTitleLines.size() * LINE_HEIGHT;
// 计算图片组的自适应高度 // 计算图片组的自适应高度
@ -122,24 +176,263 @@ public class PDFGenerator {
} }
} }
} }
}
} finally { } finally {
cs.close(); cs.close();
} }
// document.save("D:/output.pdf");
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 将PDF保存到输出流
document.save(baos); document.save(outputStream);
outputStream.flush(); }
baos.writeTo(outputStream); }
// 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) { private static List<String> wrapText(String text, float maxWidth) {
List<String> lines = new ArrayList<>(); 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(); StringBuilder currentLine = new StringBuilder();
for (String word : text.split("")) { String[] words = text.split("");
for (String word : words) {
if (currentLine.length() + word.length() <= maxCharsPerLine) { if (currentLine.length() + word.length() <= maxCharsPerLine) {
currentLine.append(word); currentLine.append(word);
} else { } else {
@ -152,6 +445,24 @@ public class PDFGenerator {
} }
return lines; 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 { private static float calculateTotalHeight(List<String> imageUrls) throws IOException {
@ -261,7 +572,7 @@ public class PDFGenerator {
// 单图片模式:一页两行,每行占半页高度 // 单图片模式:一页两行,每行占半页高度
float singleImageHeight = imageAvailableHeight; 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; // float titleY = startY - CELL_PADDING - FONT_SIZE;
cs.beginText(); cs.beginText();
@ -275,8 +586,8 @@ public class PDFGenerator {
cs.endText(); cs.endText();
// 绘制第一行图片(自动缩放) // 绘制第一行图片(自动缩放)
PDImageXObject image1 = PDImageXObject.createFromByteArray(doc, PDImageXObject image1 = loadImageFromUrl(doc, imageUrls.get(0));
new URL(imageUrls.get(0)).openStream().readAllBytes(), "embedded-image"); if (image1 != null) {
float scale1 = Math.min( float scale1 = Math.min(
imageAvailableWidth / image1.getWidth(), imageAvailableWidth / image1.getWidth(),
singleImageHeight / image1.getHeight() singleImageHeight / image1.getHeight()
@ -288,13 +599,14 @@ public class PDFGenerator {
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING, TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
startY - CELL_PADDING - scaledHeight1, startY - CELL_PADDING - scaledHeight1,
scaledWidth1, scaledHeight1); scaledWidth1, scaledHeight1);
}
} else if (imageUrls.size() == 2) { } else if (imageUrls.size() == 2) {
// 双图片模式:改为上下布局 // 双图片模式:改为上下布局
float imageHeightPer = (imageAvailableHeight - CELL_PADDING) / 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.beginText();
cs.setFont(font, FONT_SIZE); cs.setFont(font, FONT_SIZE);
cs.newLineAtOffset(TABLE_MARGIN + CELL_PADDING, titleY); cs.newLineAtOffset(TABLE_MARGIN + CELL_PADDING, titleY);
@ -306,23 +618,25 @@ public class PDFGenerator {
cs.endText(); cs.endText();
// 绘制第一张图片(上) // 绘制第一张图片(上)
PDImageXObject image1 = PDImageXObject.createFromByteArray(doc, PDImageXObject image1 = loadImageFromUrl(doc, imageUrls.get(0));
new URL(imageUrls.get(0)).openStream().readAllBytes(), "embedded-image"); float scaledHeight1 = 0;
if (image1 != null) {
float scale1 = Math.min( float scale1 = Math.min(
imageAvailableWidth / image1.getWidth(), imageAvailableWidth / image1.getWidth(),
imageHeightPer / image1.getHeight() imageHeightPer / image1.getHeight()
); );
float scaledHeight1 = image1.getHeight() * scale1; scaledHeight1 = image1.getHeight() * scale1;
float scaledWidth1 = image1.getWidth() * scale1; float scaledWidth1 = image1.getWidth() * scale1;
cs.drawImage(image1, cs.drawImage(image1,
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING, TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
startY - CELL_PADDING - scaledHeight1, startY - CELL_PADDING - scaledHeight1,
scaledWidth1, scaledHeight1); scaledWidth1, scaledHeight1);
}
// 绘制第二张图片(下) // 绘制第二张图片(下)
PDImageXObject image2 = PDImageXObject.createFromByteArray(doc, PDImageXObject image2 = loadImageFromUrl(doc, imageUrls.get(1));
new URL(imageUrls.get(1)).openStream().readAllBytes(), "embedded-image"); if (image2 != null) {
float scale2 = Math.min( float scale2 = Math.min(
imageAvailableWidth / image2.getWidth(), imageAvailableWidth / image2.getWidth(),
imageHeightPer / image2.getHeight() imageHeightPer / image2.getHeight()
@ -334,6 +648,7 @@ public class PDFGenerator {
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING, TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING,
startY - CELL_PADDING - scaledHeight1 - CELL_PADDING - scaledHeight2, startY - CELL_PADDING - scaledHeight1 - CELL_PADDING - scaledHeight2,
scaledWidth2, scaledHeight2); scaledWidth2, scaledHeight2);
}
/* // 双图片模式:一行并列显示 /* // 双图片模式:一行并列显示
float imageWidthPer = (imageAvailableWidth - CELL_PADDING) / 2; float imageWidthPer = (imageAvailableWidth - CELL_PADDING) / 2;
@ -378,8 +693,239 @@ public class PDFGenerator {
TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING + imageWidthPer + CELL_PADDING, TABLE_MARGIN + LEFT_COL_WIDTH + CELL_PADDING + imageWidthPer + CELL_PADDING,
startY - CELL_PADDING - scaledHeight2, startY - CELL_PADDING - scaledHeight2,
scaledWidth2, 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, private static float calculateAdjustedImagesHeight(PDDocument doc, List<String> imageUrls,
float maxWidth, float maxHeight) throws IOException { float maxWidth, float maxHeight) throws IOException {
if (imageUrls.isEmpty()) return 0; if (imageUrls.isEmpty()) return 0;
@ -388,14 +934,28 @@ public class PDFGenerator {
if (imageUrls.size() == 1) { if (imageUrls.size() == 1) {
// 单图片模式:占用半页高度 // 单图片模式:占用半页高度
return maxHeight / 2; return maxHeight / 2;
} else { } else if (imageUrls.size() == 2) {
// 双图片模式:整页高度 // 双图片模式:整页高度
return maxHeight; 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 = ...; // 获取字体对象 // PDType0Font font = ...; // 获取字体对象
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
float currentWidth = 0; float currentWidth = 0;
@ -421,8 +981,8 @@ public class PDFGenerator {
} }
// 简单估算:假设每个中文字符宽度等于字体大小 // 简单估算:假设每个中文字符宽度等于字体大小
int charsPerLine = (int)(maxWidth / fontSize); int charsPerLine = (int) (maxWidth / fontSize);
int lineCount = (int)Math.ceil((double)text.length() / charsPerLine); int lineCount = (int) Math.ceil((double) text.length() / charsPerLine);
return lineCount * fontSize * 1.2f; // 行间距1.2倍 return lineCount * fontSize * 1.2f; // 行间距1.2倍
} }
@ -435,7 +995,7 @@ public class PDFGenerator {
cs.setLeading(fontSize * 1.2f); // 行间距 cs.setLeading(fontSize * 1.2f); // 行间距
// 简单换行实现 // 简单换行实现
int charsPerLine = (int)(maxWidth / (fontSize * 0.8)); // 中文字符估算 int charsPerLine = (int) (maxWidth / (fontSize * 0.8)); // 中文字符估算
int pos = 0; int pos = 0;
float currentY = startY; float currentY = startY;
@ -452,6 +1012,7 @@ public class PDFGenerator {
currentY -= fontSize * 1.2f; currentY -= fontSize * 1.2f;
} }
} }
// 图片高度预估方法 // 图片高度预估方法
private static float estimateImageHeight(String imageUrl, float targetWidth) throws IOException { private static float estimateImageHeight(String imageUrl, float targetWidth) throws IOException {
try (InputStream is = new URL(imageUrl).openStream()) { try (InputStream is = new URL(imageUrl).openStream()) {
@ -589,8 +1150,6 @@ public class PDFGenerator {
}*/ }*/
private static void drawTable(PDPageContentStream cs, PDType0Font font, private static void drawTable(PDPageContentStream cs, PDType0Font font,
float x, float y, String[][] data, float[] colWidths) throws IOException { float x, float y, String[][] data, float[] colWidths) throws IOException {
@ -615,7 +1174,7 @@ public class PDFGenerator {
// 绘制列分隔线 // 绘制列分隔线
float currentX = x; float currentX = x;
for (int i = 1; i < colWidths.length; i++) { for (int i = 1; i < colWidths.length; i++) {
currentX += colWidths[i-1]; currentX += colWidths[i - 1];
cs.moveTo(currentX, y); cs.moveTo(currentX, y);
cs.lineTo(currentX, y - ROW_HEIGHT * data.length); cs.lineTo(currentX, y - ROW_HEIGHT * data.length);
cs.stroke(); cs.stroke();
@ -652,13 +1211,13 @@ public class PDFGenerator {
} }
private static float addImageRow(PDPageContentStream cs, PDDocument doc, private static float addImageRow(PDPageContentStream cs, PDDocument doc,
PDType0Font font, String title, String imageUrl, PDType0Font font, String title, String imageUrl,
float startX, float startY) throws IOException { float startX, float startY) throws IOException {
// 加载图片 // 加载图片
PDImageXObject image = PDImageXObject.createFromByteArray(doc, PDImageXObject image = loadImageFromUrl(doc, imageUrl);
new URL(imageUrl).openStream().readAllBytes(), "embedded-image"); if (image == null) return startY; // 如果图片加载失败,返回原位置
// 动态计算尺寸 // 动态计算尺寸
float imageWidth = RIGHT_COL_WIDTH - 10; float imageWidth = RIGHT_COL_WIDTH - 10;
float scale = imageWidth / image.getWidth(); float scale = imageWidth / image.getWidth();
@ -672,7 +1231,7 @@ public class PDFGenerator {
cs.stroke(); cs.stroke();
// 垂直居中文本(改进算法) // 垂直居中文本(改进算法)
float textY = startY - rowHeight/2 + FONT_SIZE/2; float textY = startY - rowHeight / 2 + FONT_SIZE / 2;
//String title = "安装完成探测器工作图片\n检测时间2025-08-14"; //String title = "安装完成探测器工作图片\n检测时间2025-08-14";
/* if(title.contains("\n")){ /* if(title.contains("\n")){
@ -680,7 +1239,7 @@ public class PDFGenerator {
textY = startY - rowHeight/2 + FONT_SIZE/2 + (lines.length-1)*FONT_SIZE/2; textY = startY - rowHeight/2 + FONT_SIZE/2 + (lines.length-1)*FONT_SIZE/2;
}*/ }*/
drawCenteredText(cs, font, FONT_SIZE, title, drawCenteredText(cs, font, FONT_SIZE, title,
startX + LEFT_COL_WIDTH/2, textY); startX + LEFT_COL_WIDTH / 2, textY);
// 绘制图片带5pt边距 // 绘制图片带5pt边距
cs.drawImage(image, cs.drawImage(image,
@ -725,6 +1284,79 @@ public class PDFGenerator {
return startY - dynamicRowHeight - 10;*/ 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, /* private static float addImageRow(PDPageContentStream cs, PDDocument doc,
PDType0Font font, String title, String imageUrl, float x, float y) throws IOException { 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.data.manager.IDeviceInstallInfoData;
import cc.iotkit.manager.config.GasInstallationPDFGenerator; import cc.iotkit.manager.config.GasInstallationPDFGenerator;
import cc.iotkit.manager.config.PDFGenerator; 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.DeviceDetectorQueryBo;
import cc.iotkit.manager.dto.bo.device.DeviceInstallInfoQueryBo; import cc.iotkit.manager.dto.bo.device.DeviceInstallInfoQueryBo;
import cc.iotkit.manager.dto.bo.device.DeviceQueryBo; import cc.iotkit.manager.dto.bo.device.DeviceQueryBo;
@ -100,6 +102,11 @@ import java.net.URLEncoder;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Collections; import java.util.Collections;
import java.util.List; 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 cc.iotkit.data.model.QTbDeviceInstallInfo.tbDeviceInstallInfo;
import static com.itextpdf.kernel.pdf.PdfName.*; import static com.itextpdf.kernel.pdf.PdfName.*;
@ -116,6 +123,12 @@ public class DeviceInstallInfoController {
@Autowired @Autowired
private IDeviceInstallInfoData iDeviceInstallInfoData; private IDeviceInstallInfoData iDeviceInstallInfoData;
@Autowired
private ExecutorService taskExecutor;
// 存储任务状态
private static final ConcurrentHashMap<String, ExportTask> exportTasks = new ConcurrentHashMap<>();
@ApiOperation(value = "安装列表", notes = "设备列表", httpMethod = "POST") @ApiOperation(value = "安装列表", notes = "设备列表", httpMethod = "POST")
//@SaCheckPermission("iot:device:query") //@SaCheckPermission("iot:device:query")
@PostMapping("/page") @PostMapping("/page")
@ -201,6 +214,57 @@ public class DeviceInstallInfoController {
PDFGenerator.generateInstallationPDF(data, response.getOutputStream()); 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("导出设备列表") @ApiOperation("导出设备列表")
@Log(title = "导出设备列表", businessType = BusinessType.EXPORT) @Log(title = "导出设备列表", businessType = BusinessType.EXPORT)
// @SaCheckPermission("system:user:export")@RequestBody Request<ProductBo> // @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 endTime;
private String config; private String config;
//设备状态0正常1故障2报警3离线
private Integer deviceStatus;
} }

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

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

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

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

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

@ -148,13 +148,14 @@ public class DeviceManagerServiceImpl implements IDeviceManagerService {
String name = query.getName(); String name = query.getName();
String deviceName = query.getDeviceName(); String deviceName = query.getDeviceName();
String pk = query.getProductKey(); String pk = query.getProductKey();
Integer deviceStatus = query.getDeviceStatus();
//关键字查询 //关键字查询
String keyword = query.getKeyword(); String keyword = query.getKeyword();
String group = query.getGroup(); String group = query.getGroup();
Boolean online = query.getOnline(); Boolean online = query.getOnline();
Long areaDepeId = query.getDeptAreaId(); Long areaDepeId = query.getDeptAreaId();
Paging<DeviceInfoVo> result = MapstructUtils.convert(deviceInfoData.findByConditions(name, uid, subUid, pk, group, 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(); List<Category> categorys = categoryData.findAll();
for (DeviceInfoVo row : result.getRows()) { for (DeviceInfoVo row : result.getRows()) {
if (ObjectUtil.isNotEmpty(row.getProductKey())) { 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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.security.PermitAll; import javax.annotation.security.PermitAll;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -163,6 +164,7 @@ public class ThingServiceImpl implements IThingService {
switch (type) { switch (type) {
case REGISTER: case REGISTER:
//设备注册 //设备注册
// 根据
String deviceId = registerDevice(device, (DeviceRegister) action, null); String deviceId = registerDevice(device, (DeviceRegister) action, null);
deviceUpdateLastTime(deviceId, lastTime); deviceUpdateLastTime(deviceId, lastTime);
@ -367,8 +369,9 @@ public class ThingServiceImpl implements IThingService {
.deviceStatus(device.getDeviceStatus()) .deviceStatus(device.getDeviceStatus())
.occurred(System.currentTimeMillis()) .occurred(System.currentTimeMillis())
.build(); .build();
//增加启用未启用条件查询
List<AlertConfig> config = iAlertConfigData.findByDeviceName(device.getDeviceName()); List<AlertConfig> config = iAlertConfigData.findByDeviceName(device.getDeviceName());
//在设备列表中,添加了手机号并且选择了通知方式、语音、短信等。其实是添加到了告警事件配置里面。
// List<AlertRecord> alerts = idAlertRecordData.selectAlertConfigPage(AlertRecordBo.builder().details(device.getDeviceName()).build()); // List<AlertRecord> alerts = idAlertRecordData.selectAlertConfigPage(AlertRecordBo.builder().details(device.getDeviceName()).build());
System.out.println("设备告警 您的设备【" + device.getDeviceName() + "】触发事件 事件类型为:【" + map.get("eventTypeValue").toString() + "】。"); System.out.println("设备告警 您的设备【" + device.getDeviceName() + "】触发事件 事件类型为:【" + map.get("eventTypeValue").toString() + "】。");
if (ObjectUtil.isEmpty(config)) { if (ObjectUtil.isEmpty(config)) {
@ -379,7 +382,12 @@ public class ThingServiceImpl implements IThingService {
executor.execute(() -> { executor.execute(() -> {
List<Rule> rules = ruleMessageHandler.processMessage1(thingModelMessage); List<Rule> rules = ruleMessageHandler.processMessage1(thingModelMessage);
for (Rule rule : rules) { 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++) { for (int i = 0; i < config1.size(); i++) {
idAlertRecordData.save(AlertRecord.builder() idAlertRecordData.save(AlertRecord.builder()
.level(config1.get(i).getLevel()) .level(config1.get(i).getLevel())
@ -394,28 +402,28 @@ public class ThingServiceImpl implements IThingService {
.deptAreaId(device.getDeptAreaId()) .deptAreaId(device.getDeptAreaId())
.readFlg(false) .readFlg(false)
.alertTime(System.currentTimeMillis()) .alertTime(System.currentTimeMillis())
.details("您的设备【" + device.getDeviceName() + "】触发事件 事件类型为:【" + map.get("eventTypeValue").toString() + "】。") .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()); .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); ruleExecutor.execute1(thingModelMessage, config1);
} }
} }
@ -443,28 +451,28 @@ public class ThingServiceImpl implements IThingService {
.deptAreaId(device.getDeptAreaId()) .deptAreaId(device.getDeptAreaId())
.readFlg(false) .readFlg(false)
.alertTime(System.currentTimeMillis()) .alertTime(System.currentTimeMillis())
.details("您的设备【" + device.getDeviceName() + "】触发事件 事件类型为:【" + map.get("eventTypeValue").toString() + "】。") .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()); .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.execute1(thingModelMessage, config);
} }
// RuleExecutor ruleExecutor = new RuleExecutor(); // RuleExecutor ruleExecutor = new RuleExecutor();
@ -491,7 +499,21 @@ public class ThingServiceImpl implements IThingService {
flag = false; flag = false;
if (deviceInfo2.getNodeOneStatus().equals("1")) { 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()); flag = deviceLinkageControlSend(deviceInfo2, deviceInfo2.getNodeOne());
}
} else { } else {
//满足所有的设备告警才触发联动控制 //满足所有的设备告警才触发联动控制
String[] nodeOneDevice = deviceInfo2.getNodeOneDevice().split(","); String[] nodeOneDevice = deviceInfo2.getNodeOneDevice().split(",");
@ -513,7 +535,21 @@ public class ThingServiceImpl implements IThingService {
flag = false; flag = false;
if (deviceInfo2.getNodeTwoStatus().equals("1")) { 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 { } else {
//满足所有的设备告警才触发联动控制 //满足所有的设备告警才触发联动控制
String[] nodeOneDevice = deviceInfo2.getNodeTwoDevice().split(","); String[] nodeOneDevice = deviceInfo2.getNodeTwoDevice().split(",");
@ -532,7 +568,21 @@ public class ThingServiceImpl implements IThingService {
flag = false; flag = false;
if (deviceInfo2.getNodeThreeStatus().equals("1")) { 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 { } else {
//满足所有的设备告警才触发联动控制 //满足所有的设备告警才触发联动控制
String[] nodeOneDevice = deviceInfo2.getNodeThreeDevice().split(","); String[] nodeOneDevice = deviceInfo2.getNodeThreeDevice().split(",");
@ -551,7 +601,21 @@ public class ThingServiceImpl implements IThingService {
flag = false; flag = false;
if (deviceInfo2.getNodeFourStatus().equals("1")) { 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 { } else {
//满足所有的设备告警才触发联动控制 //满足所有的设备告警才触发联动控制
String[] nodeOneDevice = deviceInfo2.getNodeFourDevice().split(","); String[] nodeOneDevice = deviceInfo2.getNodeFourDevice().split(",");
@ -570,7 +634,21 @@ public class ThingServiceImpl implements IThingService {
flag = false; flag = false;
if (deviceInfo2.getNodeFiveStatus().equals("1")) { 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 { } else {
//满足所有的设备告警才触发联动控制 //满足所有的设备告警才触发联动控制
String[] nodeOneDevice = deviceInfo2.getNodeFiveDevice().split(","); String[] nodeOneDevice = deviceInfo2.getNodeFiveDevice().split(",");
@ -667,12 +745,27 @@ public class ThingServiceImpl implements IThingService {
"controlOutput", node + "0001" "controlOutput", node + "0001"
)) ))
.build(); .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); deviceService.invoke(thingService);
return true; return true;
} }
//浓度变化 //浓度变化,事件类型:
public void extractDetectorData(DeviceInfo device, Map<String, Object> baseInfo) { public void extractDetectorData(DeviceInfo device, Map<String, Object> baseInfo) {
for (Map.Entry<String, Object> entry : baseInfo.entrySet()) { for (Map.Entry<String, Object> entry : baseInfo.entrySet()) {
if (entry.getKey().startsWith("dataDetectorValue")) { if (entry.getKey().startsWith("dataDetectorValue")) {
@ -887,7 +980,7 @@ public class ThingServiceImpl implements IThingService {
if (productKey != null && device.getProductKey() != null if (productKey != null && device.getProductKey() != null
&& !device.getProductKey().equals(productKey)) { && !device.getProductKey().equals(productKey)) {
//防止设备变更了产品key //防止设备变更了产品key
device.setProductKey(productKey); // device.setProductKey(productKey);
// device.setModel(register.getModel()); // device.setModel(register.getModel());
deviceInfoData.save(device); deviceInfoData.save(device);
} }

@ -28,10 +28,12 @@ import cc.iotkit.common.thing.ThingModelMessage;
import cc.iotkit.common.utils.JsonUtils; import cc.iotkit.common.utils.JsonUtils;
import cc.iotkit.data.manager.IAlertConfigData; import cc.iotkit.data.manager.IAlertConfigData;
import cc.iotkit.data.manager.IDeviceInfoData; import cc.iotkit.data.manager.IDeviceInfoData;
import cc.iotkit.data.manager.IProductData;
import cc.iotkit.data.manager.IUserInfoData; import cc.iotkit.data.manager.IUserInfoData;
import cc.iotkit.model.UserInfo; import cc.iotkit.model.UserInfo;
import cc.iotkit.model.alert.AlertConfig; import cc.iotkit.model.alert.AlertConfig;
import cc.iotkit.model.device.DeviceInfo; import cc.iotkit.model.device.DeviceInfo;
import cc.iotkit.model.product.Product;
import cc.iotkit.model.rule.RuleLog; import cc.iotkit.model.rule.RuleLog;
import cc.iotkit.model.worder.WorderDO; import cc.iotkit.model.worder.WorderDO;
import cc.iotkit.ruleengine.action.Action; 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.core.factory.SmsFactory;
import org.dromara.sms4j.provider.enumerate.SupplierType; import org.dromara.sms4j.provider.enumerate.SupplierType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.security.auth.message.config.AuthConfig; import javax.security.auth.message.config.AuthConfig;
@ -100,6 +104,14 @@ public class RuleExecutor {
@Resource @Resource
private WxMaService wxMaService; private WxMaService wxMaService;
@Autowired
@Qualifier("productDataCache")
private IProductData productData;
@Autowired
@Qualifier("deviceInfoDataCache")
private IDeviceInfoData deviceInfoData;
public void execute1(ThingModelMessage message,List<AlertConfig> listAlertConfigs) { public void execute1(ThingModelMessage message,List<AlertConfig> listAlertConfigs) {
DeviceInfo deviceInfo = iDeviceInfoData.findByDeviceId(message.getDeviceId()); DeviceInfo deviceInfo = iDeviceInfoData.findByDeviceId(message.getDeviceId());
// 注入 WxMaService 后直接调用 // 注入 WxMaService 后直接调用
@ -279,6 +291,7 @@ public class RuleExecutor {
case "0": case "0":
//发送短信 //发送短信
try { try {
Date date = new Date(message.getTime()); Date date = new Date(message.getTime());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String templateId = "SMS_492470379"; String templateId = "SMS_492470379";
@ -303,6 +316,23 @@ public class RuleExecutor {
System.out.println("短信接口返回的数据:" + smsResponse); System.out.println("短信接口返回的数据:" + smsResponse);
/* if (!"OK".equals(smsResponse.getCode())) { /* 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) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -518,4 +548,98 @@ public class RuleExecutor {
return results; 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(); SysRole querySysRole=new SysRole();
querySysRole.setTenantId(tenantId); 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())); sysRoleData.deleteByIds(roles.stream().map(SysRole::getId).collect(Collectors.toList()));
//删除部门 //删除部门
List<SysDept> lsDepts = sysDeptData.findByTenantId(tenantId); 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.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Configuration @Configuration
@EnableScheduling @EnableScheduling
public class SchedulerConfig { public class SchedulerConfig {
@ -41,4 +44,10 @@ public class SchedulerConfig {
return scheduler; return scheduler;
} }
@Bean
public ExecutorService taskExecutor() {
// 创建一个固定大小的线程池大小为10
return Executors.newFixedThreadPool(10);
}
} }

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

@ -72,6 +72,12 @@
<appender-ref ref="console"/> <appender-ref ref="console"/>
</logger> </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.hibernate.type.descriptor.sql.BasicBinder" level="trace"/>
<!-- <logger name="org.springframework" level="debug" additivity="false">--> <!-- <logger name="org.springframework" level="debug" additivity="false">-->

Loading…
Cancel
Save