diff --git a/pom.xml b/pom.xml index aec9730..ebf41ae 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,19 @@ + + + com.itextpdf + kernel + 7.2.5 + + + com.itextpdf + layout + 7.2.5 + + + org.springframework.boot spring-boot-starter-web diff --git a/src/main/java/com/whdc/controller/AutoCallController.java b/src/main/java/com/whdc/controller/AutoCallController.java index 64e1f1e..51c6944 100644 --- a/src/main/java/com/whdc/controller/AutoCallController.java +++ b/src/main/java/com/whdc/controller/AutoCallController.java @@ -1,6 +1,10 @@ package com.whdc.controller; +import cn.hutool.http.HttpResponse; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.itextpdf.io.font.PdfEncodings; +import com.itextpdf.kernel.font.PdfFont; +import com.itextpdf.kernel.font.PdfFontFactory; import com.whdc.model.dto.AutoCallDto; import com.whdc.model.entity.AutoCallPerson; import com.whdc.model.entity.AutoCallTask; @@ -9,9 +13,15 @@ import com.whdc.service.AutoCallApiService; import com.whdc.service.AutoCallTaskService; import com.whdc.service.AutoCallTaskService2; import com.whdc.utils.ResultJson; +import org.apache.poi.util.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.URLEncoder; import java.text.ParseException; import java.util.List; @@ -88,4 +98,38 @@ public class AutoCallController { autoCallTaskService.setCallIsPut(taskId); return ResultJson.ok(true); } + + @PostMapping("/exportPdf") + public void exportPDF(@RequestBody AutoCallDto dto, + HttpServletResponse response, + HttpServletRequest request) { + Page autoCallTaskPage = autoCallApiService.page3(dto); + List records = autoCallTaskPage.getRecords(); + ByteArrayInputStream pdfStream = autoCallApiService.exportPDF(records, response, dto.getStm(), dto.getEtm()); + // 3. 设置响应头 + try { + // 设置响应头 + response.setContentType("application/pdf"); + String fileName = "智能呼叫记录_" + dto.getStm().toString().replace(":", "-") + "_to_" + + dto.getEtm().toString().replace(":", "-") + ".pdf"; + + // 获取User-Agent判断浏览器类型 + String userAgent = request.getHeader("User-Agent"); + + // 针对不同浏览器设置不同的Content-Disposition + if (userAgent != null && userAgent.toLowerCase().contains("firefox")) { + // Firefox浏览器 + response.setHeader("Content-Disposition", + "attachment; filename*=UTF-8''" + URLEncoder.encode(fileName, "UTF-8")); + } else { + // 其他浏览器(Chrome, Edge, Safari等) + response.setHeader("Content-Disposition", + "attachment; filename=\"" + new String(fileName.getBytes("GBK"), "ISO-8859-1") + "\""); + } + IOUtils.copy(pdfStream,response.getOutputStream()); + response.flushBuffer(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/whdc/mapper/AutoCallPersonMapper.java b/src/main/java/com/whdc/mapper/AutoCallPersonMapper.java index dd766fb..ba23db8 100644 --- a/src/main/java/com/whdc/mapper/AutoCallPersonMapper.java +++ b/src/main/java/com/whdc/mapper/AutoCallPersonMapper.java @@ -3,6 +3,7 @@ package com.whdc.mapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.whdc.model.entity.AutoCallPerson; +import org.apache.ibatis.annotations.Param; import java.util.List; @@ -39,4 +40,9 @@ public interface AutoCallPersonMapper extends BaseMapper { ); return personCnt == failCnt; } + + List selectListByTaskId(@Param("taskId") Integer taskId); + + String selectPositionByPhone(@Param("phone") String uploadNumber); + } diff --git a/src/main/java/com/whdc/model/entity/AutoCallPerson.java b/src/main/java/com/whdc/model/entity/AutoCallPerson.java index e2e6eda..b335b0a 100644 --- a/src/main/java/com/whdc/model/entity/AutoCallPerson.java +++ b/src/main/java/com/whdc/model/entity/AutoCallPerson.java @@ -76,4 +76,6 @@ public class AutoCallPerson { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @TableField(value = "__last_modify") private Date detailLastModify; + @TableField(exist = false) + private String position; } diff --git a/src/main/java/com/whdc/service/AutoCallApiService.java b/src/main/java/com/whdc/service/AutoCallApiService.java index 1d7fa96..e0d99c2 100644 --- a/src/main/java/com/whdc/service/AutoCallApiService.java +++ b/src/main/java/com/whdc/service/AutoCallApiService.java @@ -2,6 +2,21 @@ package com.whdc.service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.itextpdf.io.font.PdfEncodings; +import com.itextpdf.kernel.colors.ColorConstants; +import com.itextpdf.kernel.font.PdfFont; +import com.itextpdf.kernel.font.PdfFontFactory; +import com.itextpdf.kernel.geom.PageSize; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.layout.Document; +import com.itextpdf.layout.borders.Border; +import com.itextpdf.layout.element.Cell; +import com.itextpdf.layout.element.Div; +import com.itextpdf.layout.element.Paragraph; +import com.itextpdf.layout.element.Table; +import com.itextpdf.layout.properties.TextAlignment; +import com.itextpdf.layout.properties.UnitValue; import com.whdc.mapper.*; import com.whdc.model.dto.AutoCallDto; import com.whdc.model.entity.AutoCall; @@ -13,10 +28,16 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.text.ParseException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.List; + /** * @author lyf * @since 2025-06-14 @@ -29,6 +50,8 @@ public class AutoCallApiService { @Autowired private WarnCallMapMapper warnCallMapMapper; + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + public Page page2(AutoCallDto dto) { Date stm = null; Date etm = null; @@ -141,7 +164,6 @@ public class AutoCallApiService { } return wcmList; } - @Autowired private AutoCallTaskMapper taskMapper; @Autowired @@ -149,4 +171,239 @@ public class AutoCallApiService { @Autowired private QXWarningMapper warningMapper; + + public Page page3(AutoCallDto dto) { + Date stm = null; + Date etm = null; + try { + if (dto.getStm() != null && dto.getEtm() != null) { + stm = DateUtils.formatD(dto.getStm(), DateUtils.DATE_PATTERN1); + etm = DateUtils.formatD(dto.getEtm(), DateUtils.DATE_PATTERN1); + } + } catch (ParseException e) { + throw new RuntimeException(e); + } + if (stm != null) stm = DateUtils.standardize(stm, true); + if (etm != null) etm = DateUtils.standardize(etm, false); + + QueryWrapper query = new QueryWrapper() + .orderByDesc("id") + .between("warn_tm", stm, etm); + if (dto.getCallIsPut() != null) { + if (dto.getCallIsPut()) { + query.eq("status", + AutoCallTask.STATUS_ANY_SUCCESS + ); + } else { + query.eq("status", + AutoCallTask.STATUS_ALL_FAIL + ); + } + } else { + query.in("status", + AutoCallTask.STATUS_SHOULD_GENERATE, + AutoCallTask.STATUS_GENERATED, + AutoCallTask.STATUS_ANY_SUCCESS, + AutoCallTask.STATUS_ALL_FAIL, + AutoCallTask.STATUS_CANCELLED + ); + } + Page pageParam = dto.getPage().getPage(); + Page pageResult = taskMapper.selectPage( + pageParam, + query + ); + + List records = pageResult.getRecords(); + if (records.size() > 0) { + for (AutoCallTask task : records) { + Integer taskId = task.getId(); + List autoCallList = personMapper.selectList( + new QueryWrapper() + .eq("task_id", taskId) + ); + for (AutoCallPerson autoCallPerson : autoCallList) { + String position = personMapper.selectPositionByPhone(autoCallPerson.getUploadNumber()); + autoCallPerson.setPosition(position); + } + task.setCallList(autoCallList); + } + } + return pageResult; + } + + /** + * pdf导出 + * @param records + * @param response + * @param stm + * @param etm + * @return + */ + public ByteArrayInputStream exportPDF(List records, HttpServletResponse response, String stm, String etm) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (PdfWriter writer = new PdfWriter(out); + // 设置A4横向(宽297mm,高210mm) + PdfDocument pdfDoc = new PdfDocument(writer); + Document document = new Document(pdfDoc, PageSize.A4.rotate())) { + + // 添加标题 + String fontPath = "/fonts/ChineseFonts.ttf"; + PdfFont font = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H); + document.setFont(font); + document.add(new Paragraph("智能呼叫记录报表") + .setFontSize(16) + .setBold() + .setTextAlignment(TextAlignment.CENTER) + .setMarginBottom(20)); + + // 创建头部布局(时间段和签名) + Div headerDiv = new Div().setMarginBottom(15); + float[] columnWidths1 = {1, 1}; + Table layoutTable = new Table(columnWidths1) + .setWidth(UnitValue.createPercentValue(100)) + .setMarginBottom(15); + + Paragraph leftPara = new Paragraph(String.format("筛选时间段:%s 至 %s", stm, etm)) + .setFontSize(12) + .setTextAlignment(TextAlignment.LEFT); + Paragraph rightPara = new Paragraph("值班人员签名:________________________") + .setFontSize(12) + .setTextAlignment(TextAlignment.RIGHT); + + layoutTable.addCell(new Cell().add(leftPara).setBorder(Border.NO_BORDER)); + layoutTable.addCell(new Cell().add(rightPara).setBorder(Border.NO_BORDER)); + document.add(layoutTable); + + // 定义表格列宽(适配横向A4,新增字段列) + float[] columnWidths = { + 30, // 序号 + 150, // 预警名称 + 120, // 智能呼叫时间 + 100, // 县(市、区) + 130, // 状态 + 59, // 通话时长(秒) + 150, // 拨打时间 + 150, // 结束时间 + 100 // 呼叫对象及职务 + }; + Table table = new Table(columnWidths); + table.setSkipFirstHeader(false); + // 添加表头 + addTableHeader(table); + + + // 添加数据行 + addTableData(table, records); + + document.add(table); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + return new ByteArrayInputStream(out.toByteArray()); + } +// 更新表头(添加新字段) +private void addTableHeader(Table table) { + String[] headers = { + "序号", + "预警名称", + "智能呼叫时间", + "县(市、区)", + "状态", + "通话时长", + "拨打时间", + "结束时间", + "呼叫对象及职务" + }; + for (String header : headers) { + table.addHeaderCell(new Cell() + .add(new Paragraph(header)) + .setBackgroundColor(ColorConstants.LIGHT_GRAY) + .setTextAlignment(TextAlignment.CENTER)); + } +} + + // 更新数据行(适配新字段) + private void addTableData(Table table, List records) { + int serialNumber = 1; + for (AutoCallTask record : records) { + // 拼接县(市、区) + String region = record.getWarnCtnm() + record.getWarnCnnm(); + + // 处理呼叫对象信息 + StringBuilder callInfo = new StringBuilder(); + for (AutoCallPerson item : record.getCallList()) { + String name = item.getUploadCustName() != null ? item.getUploadCustName() : "未知对象"; + String position = item.getPosition() != null ? item.getPosition() : "无职务"; + callInfo.append(name).append("(").append(position).append(")\n"); + } + //处理通话时长 + StringBuilder callTime = new StringBuilder(); + for (AutoCallPerson item : record.getCallList()) { + String time = item.getDetailTalkTimes() != null? item.getDetailTalkTimes() + "秒" : "未知"; + callTime.append(time).append("\n"); + } + //处理状态 + StringBuilder status = new StringBuilder(); + for (AutoCallPerson item : record.getCallList()) { + String s = item.getDetailRemark() != null? item.getDetailRemark() : "未知"; + status.append(s).append("\n"); + } + //处理拨打时间 + StringBuilder startTime = new StringBuilder(); + for (AutoCallPerson item : record.getCallList()) { + String s = formatTime(item.getDetailStartedAt()); + startTime.append(s).append("\n"); + } + //处理结束拨打时间 + StringBuilder endTime = new StringBuilder(); + for (AutoCallPerson item : record.getCallList()) { + String s = formatTime(item.getDetailEndringAt()); + endTime.append(s).append("\n"); + } + // 格式化时间 + String formattedCreateTime = formatTime(record.getCreateTm()); + + // 添加行数据 + table.addCell(new Cell().add(new Paragraph(String.valueOf(serialNumber++)).setFontSize(10))); + table.addCell(new Cell().add(new Paragraph(record.getWarnName() != null ? record.getWarnName() : "").setFontSize(10))); + table.addCell(new Cell().add(new Paragraph(formattedCreateTime).setFontSize(10))); + table.addCell(new Cell().add(new Paragraph(region).setFontSize(10))); + table.addCell(new Cell().add(new Paragraph(status.toString()).setFontSize(10))); + table.addCell(new Cell().add(new Paragraph(callTime.toString()).setFontSize(10))); + table.addCell(new Cell().add(new Paragraph(startTime.toString()).setFontSize(10))); + table.addCell(new Cell().add(new Paragraph(endTime.toString()).setFontSize(10))); + table.addCell(new Cell().add(new Paragraph(callInfo.toString()).setFontSize(10))); + } + } + + // 新增时间格式化工具方法 + private String formatTime(Object timeObj) { + if (timeObj == null) { + return ""; + } + + // 处理LocalDateTime类型 + if (timeObj instanceof LocalDateTime) { + return ((LocalDateTime) timeObj).format(DATE_TIME_FORMATTER); + } + // 处理Date类型 + else if (timeObj instanceof Date) { + LocalDateTime dateTime = new java.sql.Timestamp( + ((Date) timeObj).getTime() + ).toLocalDateTime(); + return dateTime.format(DATE_TIME_FORMATTER); + } + // 处理其他可能的时间类型 + else if (timeObj instanceof Long) { // 时间戳 + LocalDateTime dateTime = new java.sql.Timestamp( + (Long) timeObj + ).toLocalDateTime(); + return dateTime.format(DATE_TIME_FORMATTER); + } + + // 无法识别的类型直接返回字符串 + return timeObj.toString(); + } } diff --git a/src/main/resources/fonts/ChineseFonts.ttf b/src/main/resources/fonts/ChineseFonts.ttf new file mode 100644 index 0000000..4c2831f Binary files /dev/null and b/src/main/resources/fonts/ChineseFonts.ttf differ diff --git a/src/main/resources/mapper/AutoCallPersonMapper.xml b/src/main/resources/mapper/AutoCallPersonMapper.xml new file mode 100644 index 0000000..e184077 --- /dev/null +++ b/src/main/resources/mapper/AutoCallPersonMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + \ No newline at end of file