feat: 导出pdf功能调整

This commit is contained in:
曹鹏飞 2025-03-24 16:49:32 +08:00
parent 658353d636
commit d076a18b12
6 changed files with 69 additions and 272 deletions

View File

@ -450,106 +450,6 @@ public class TicketController extends ControllerBase {
@GetMapping("exportPdf") @GetMapping("exportPdf")
@ApiMark(moduleName = "工单管理", apiName = "导出工单为pdf") @ApiMark(moduleName = "工单管理", apiName = "导出工单为pdf")
public void exportPdf(HttpServletResponse response, @Valid @RequestParam @NotBlank(message = "工单编号不能为空") String id) { public void exportPdf(HttpServletResponse response, @Valid @RequestParam @NotBlank(message = "工单编号不能为空") String id) {
Ticket ticket = ticketService.getById(id);
AppUser user = appUserService.getById(ticket.getUserId());
List<Integer> companyIds= Arrays.stream(user.getCompanyId().split(",")).map(Integer::parseInt).collect(Collectors.toList());
List<TBaseCustomer> companys = customerService.listByIds(companyIds);
DeviceInfoVO device = deviceService.getByDeviceNo(ticket.getDeviceNo());
String handle = ticket.getHandle();
if (StrUtil.isNotBlank(handle)) {
List<AdminUser> adminUsers = adminUserService.listByIds(Arrays.stream(handle.split(",")).map(Integer::parseInt).collect(Collectors.toList()));
handle = adminUsers.stream().map(AdminUser::getUserName).collect(Collectors.joining(","));
}
List<FileInfo> images = new ArrayList<>();
List<FileInfo> files = new ArrayList<>();
if (StrUtil.isNotBlank(ticket.getAttachments())) {
StrUtil.split(ticket.getAttachments(), ",").forEach(item -> {
if (item.endsWith(".jpg") || item.endsWith(".png") || item.endsWith(".jpeg")) {
images.add(new FileInfo(item.substring(item.lastIndexOf("/")+1),urlEncode(item)));
} else {
files.add(new FileInfo(item.substring(item.lastIndexOf("/")+1),urlEncode(item)));
}
});
}
if (StrUtil.isNotBlank(ticket.getImages())) {
StrUtil.split(ticket.getImages(), ",").forEach(item -> {
images.add(new FileInfo(item.substring(item.lastIndexOf("/")+1),item));
});
}
//加上聊天中的图片和文件
List<ChatMessageVO> messageVOS=ticketChatService.getMessages(ticket.getId());
messageVOS.forEach(messageVO -> {
if (CollectionUtil.isNotEmpty(messageVO.getImages())){
messageVO.getImages().forEach(image -> {
images.add(new FileInfo(image.substring(image.lastIndexOf("/")+1),image));
});
}
if (CollectionUtil.isNotEmpty(messageVO.getAttachments())){
messageVO.getAttachments().forEach(attachment -> {
if (attachment.endsWith(".jpg") || attachment.endsWith(".png") || attachment.endsWith(".jpeg")) {
images.add(new FileInfo(attachment.substring(attachment.lastIndexOf("/")+1),urlEncode(attachment)));
} else {
files.add(new FileInfo(attachment.substring(attachment.lastIndexOf("/")+1),urlEncode(attachment)));
}
});
}
});
TicketPdfVO vo = new TicketPdfVO()
.setNo(ticket.getNo())
.setTitle(ticket.getTitle())
.setDeviceNo(ticket.getDeviceNo())
.setModelNo(device.getModelNo())
.setComponent(ticket.getComponent())
.setUseTime(ticket.getUseTime())
.setDescription(ticket.getDescription())
.setState(ticket.getState())
.setCreateUserName(user.getName())
.setCreateTime(DateTimeUtil.format(ticket.getCreateTime()))
.setCompanyName(StrUtil.join(",",companys.stream().map(TBaseCustomer::getAgencyCompanyName).collect(Collectors.toList())))
.setUrgency(ticket.getUrgency())
.setUpdateTime(DateTimeUtil.format(ticket.getUpdateTime()))
.setHandleUserName(handle)
.setReason(ticket.getReason())
.setImages(images)
.setFiles(files);
Map<String, Object> variables = new HashMap<>();
variables.put("ticket", vo);
// 渲染HTML
TemplateEngine templateEngine = new TemplateEngine();
ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
resolver.setPrefix("/templates/");
resolver.setSuffix(".html");
templateEngine.setTemplateResolver(resolver);
Context context = new Context();
context.setVariables(variables);
String html = templateEngine.process("ticketpdf", context);
response.setContentType(MediaType.APPLICATION_PDF_VALUE);
String encode = URLEncoder.encode(ticket.getTitle() + ".pdf", StandardCharsets.UTF_8);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline;filename=" +encode );
// 生成PDF
try {
ITextRenderer renderer = new ITextRenderer();
renderer.getFontResolver().addFont("fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
renderer.setDocumentFromString(html);
renderer.layout();
try (OutputStream outputStream = response.getOutputStream()) {
renderer.createPDF(outputStream);
}
} catch (Exception e) {
log.error("生成pdf出错", e);
throw new NflgException(STATE.BusinessError, "生成pdf出错");
}
}
/**
* 导出工单为pdf1
* @param id 工单id
*/
@GetMapping("exportPdf1")
@ApiMark(moduleName = "工单管理", apiName = "导出工单为pdf")
public void exportPdf1(HttpServletResponse response, @Valid @RequestParam @NotBlank(message = "工单编号不能为空") String id) {
Ticket ticket = ticketService.getById(id); Ticket ticket = ticketService.getById(id);
AppUser user = appUserService.getById(ticket.getUserId()); AppUser user = appUserService.getById(ticket.getUserId());
List<Integer> companyIds= Arrays.stream(user.getCompanyId().split(",")).map(Integer::parseInt).collect(Collectors.toList()); List<Integer> companyIds= Arrays.stream(user.getCompanyId().split(",")).map(Integer::parseInt).collect(Collectors.toList());
@ -625,7 +525,7 @@ public class TicketController extends ControllerBase {
Context context = new Context(); Context context = new Context();
context.setVariables(variables); context.setVariables(variables);
String html = templateEngine.process("ticketpdf1", context); String html = templateEngine.process("ticketpdf", context);
response.setContentType(MediaType.APPLICATION_PDF_VALUE); response.setContentType(MediaType.APPLICATION_PDF_VALUE);
String encode = URLEncoder.encode(ticket.getTitle() + ".pdf", StandardCharsets.UTF_8); String encode = URLEncoder.encode(ticket.getTitle() + ".pdf", StandardCharsets.UTF_8);

View File

@ -29,80 +29,82 @@
width: 215px; width: 215px;
height: auto; height: auto;
} }
.desc1{
text-align: center;
}
</style> </style>
</head> </head>
<body> <body>
<div>
<img style="width: 400px;height: auto;" alt="" src="https://cabinet-tool.oss-cn-hangzhou.aliyuncs.com/admin/20250207/1/mmwf/logo.png"/>
</div>
<h1>移动破售后问题反馈表</h1>
<table style="width: 100%;"> <table style="width: 100%;">
<tr> <tr>
<td class="style1">标题</td> <td colspan="3" ><img style="width: auto;height: 35px;" alt="" src="https://cabinet-tool.oss-cn-hangzhou.aliyuncs.com/admin/20250207/1/mmwf/logo.png"/></td>
<td colspan="3" th:text="${ticket.title}"></td> <td colspan="9" style="text-align: center;margin: 10px;"><span style="font-size: 16pt;font-weight: bold;">移动破售后问题反馈表</span></td>
</tr> </tr>
<tr> <tr>
<td>问题详细描述</td> <td style="width: 60px;" class="desc1">标题</td>
<td colspan="3" th:text="${ticket.description}"></td> <td colspan="7" th:text="${ticket.title}"></td>
<td colspan="2" class="desc1" style="width: 80px;">紧急程度</td>
<td colspan="2" th:text="${ticket.urgencyDesc}"></td>
</tr> </tr>
<tr> <tr>
<td>工单编号</td> <td class="desc1">设备机型</td>
<td th:text="${ticket.no}"></td> <td colspan="2" th:text="${ticket.modelNo}"></td>
<td>设备编号</td> <td style="width: 60px;" class="desc1">设备编号</td>
<td th:text="${ticket.deviceNo}"></td> <td colspan="4" th:text="${ticket.deviceNo}"></td>
<td colspan="2" class="desc1">反馈日期</td>
<td colspan="2" th:text="${ticket.createTime}"></td>
</tr> </tr>
<tr> <tr>
<td>机型</td> <td class="desc1">提交人</td>
<td th:text="${ticket.modelNo}"></td> <td colspan="2" th:text="${ticket.createUserName}"></td>
<td>使用时长</td> <td class="desc1">设备地点</td>
<td th:text="${ticket.useTime}"></td> <td colspan="4" th:text="${ticket.deviceAddress}"></td>
<td colspan="2" class="desc1">设备运行时间</td>
<td colspan="2" th:text="${ticket.useTime}+'小时'"></td>
</tr> </tr>
<tr> <tr>
<td>问题部位</td> <td class="desc1">处理人</td>
<td colspan="3" th:text="${ticket.component}"></td> <td colspan="11" th:text="${ticket.handleUserName}"></td>
</tr> </tr>
<tr> <tr>
<td>紧急程度</td> <td class="desc1">问题描述</td>
<td th:text="${ticket.urgencyDesc}"></td> <td colspan="11" th:text="${ticket.description}"></td>
<td>解决状态</td>
<td th:text="${ticket.stateDesc}"></td>
</tr> </tr>
<tr> <tr>
<td>处理人</td> <td class="desc1">图片</td>
<td th:text="${ticket.handleUserName}"></td> <td colspan="11">
<td>解决时间</td>
<td th:text="${ticket.updateTime}"></td>
</tr>
<tr>
<td>解决方案</td>
<td colspan="3" th:text="${ticket.solution}"></td>
</tr>
<tr>
<td>提交人</td>
<td th:text="${ticket.createUserName}"></td>
<td>提交时间</td>
<td th:text="${ticket.createTime}"></td>
</tr>
<tr>
<td colspan="4">文件</td>
</tr>
<tr>
<td colspan="4">
<div>
<a th:each="file:${ticket.files}" th:href="${file.url}" th:text="${file.name}"></a>
</div>
</td>
</tr>
<tr>
<td colspan="4">图片</td>
</tr>
<tr>
<td colspan="4">
<div th:each="file:${ticket.images}"> <div th:each="file:${ticket.images}">
<img class="cimg" alt="" th:src="${file.url}"/> <img class="cimg" alt="" th:src="${file.url}"/>
</div> </div>
</td> </td>
</tr> </tr>
<tr>
<td colspan="12">原因分析</td>
</tr>
<tr>
<td colspan="12" th:text="${ticket.reason}"></td>
</tr>
<tr>
<td colspan="12">解决方案</td>
</tr>
<tbody th:each="measure : ${ticket.measures}">
<tr class="desc1">
<td>NO.</td>
<td colspan="5" th:text="${measure.name}"></td>
<td>负责人</td>
<td>计划日期</td>
<td>确认日期</td>
<td colspan="3">备注</td>
</tr>
<tr th:each="item, itemStat : ${measure.items}" th:if="${not #lists.isEmpty(measure.items)}">
<td th:text="${itemStat.index + 1}" class="desc1"></td>
<td colspan="5" th:text="${item.name}"></td>
<td th:text="${item.superintendent}" class="desc1"></td>
<td th:text="${item.scheduleDate}" class="desc1"></td>
<td th:text="${item.confirmedDate}" class="desc1"></td>
<td colspan="3" th:text="${item.remark}"></td>
</tr>
</tbody>
</table> </table>
</body> </body>
</html> </html>

View File

@ -1,111 +0,0 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title th:text="${ticket.title}"></title>
<style>
body {
font-size: 12px;
font-family: SimSun, Arial, sans-serif;
font-weight: normal;
font-style: normal;
}
table, th, td {
border: black 1px solid;
border-collapse: collapse;
padding: 5px;
}
a{
margin: 5px;
}
.cimg{
margin-left: 5px;
margin-top: 5px;
float: left;
width: 215px;
height: auto;
}
</style>
</head>
<body>
<table style="width: 100%;">
<tr>
<td colspan="4"><img style="width: 100%;height: auto;" alt="" src="https://cabinet-tool.oss-cn-hangzhou.aliyuncs.com/admin/20250207/1/mmwf/logo.png"/></td>
<td colspan="8"><h1>移动破售后问题反馈表</h1></td>
</tr>
<tr>
<td>标题</td>
<td colspan="7" th:text="${ticket.title}"></td>
<td>紧急程度</td>
<td colspan="3" th:text="${ticket.urgencyDesc}"></td>
</tr>
<tr>
<td>设备机型</td>
<td colspan="2" th:text="${ticket.modelNo}"></td>
<td>设备编号</td>
<td colspan="4" th:text="${ticket.deviceNo}"></td>
<td colspan="2">反馈日期</td>
<td colspan="2" th:text="${ticket.createTime}"></td>
</tr>
<tr>
<td>提交人</td>
<td colspan="2" th:text="${ticket.createUserName}"></td>
<td>设备地点</td>
<td colspan="4" th:text="${ticket.deviceAddress}"></td>
<td colspan="2">设备运行时间</td>
<td colspan="2" th:text="${ticket.useTime}"></td>
</tr>
<tr>
<td>处理人</td>
<td colspan="11" th:text="${ticket.handleUserName}"></td>
</tr>
<tr>
<td>问题描述</td>
<td colspan="11" th:text="${ticket.description}"></td>
</tr>
<tr>
<td>图片</td>
<td colspan="11">
<div th:each="file:${ticket.images}">
<img class="cimg" alt="" th:src="${file.url}"/>
</div>
</td>
</tr>
<tr>
<td colspan="12">原因分析</td>
</tr>
<tr>
<td colspan="12" th:text="${ticket.reason}"></td>
</tr>
<tr>
<td colspan="12">解决方案</td>
</tr>
<div th:replace="::measureRow(${ticket.measures})"></div>
</table>
<div th:fragment="measureItemRow(items)">
<tr th:each="item,iterStat: ${items}">
<td th:text="${iterStat.index+1}"></td>
<td colspan="5" th:text="${item.name}"></td>
<td th:text="${item.superintendent}"></td>
<td th:text="${item.scheduleDate}"></td>
<td th:text="${item.confirmedDate}"></td>
<td colspan="3" th:text="${item.remark}"></td>
</tr>
</div>
<div th:fragment="measureRow(measures)">
<tr th:each="measure : ${measures}">
<td>NO.</td>
<td colspan="5" th:text="${measure.name}"></td>
<td>负责人</td>
<td>计划日期</td>
<td>确认日期</td>
<td colspan="3">备注</td>
</tr>
<div th:if="${measure != null and not #lists.isEmpty(measure.items)}" th:replace="::measureItemRow(${measure.items})"></div>
</div>
</body>
</html>

View File

@ -12,7 +12,7 @@ public class SolutionMeasuresItemVO {
private Integer id; private Integer id;
/** /**
* 措施名称 * 措施
*/ */
@NotBlank @NotBlank
private String name; private String name;

View File

@ -37,12 +37,17 @@ public class TicketSolution implements Serializable {
/** /**
* 字典项id * 字典项id
*/ */
private Integer dictionaryId; private Integer dictionaryItemId;
/** /**
* 字典项值 * 字典项值
*/ */
private String dictionaryName; private String dictionaryItemName;
/**
* 措施
*/
private String description;
/** /**
* 负责人 * 负责人

View File

@ -56,7 +56,7 @@ public class TicketSolutionServiceImpl extends ServiceImpl<TicketSolutionMapper,
initialNames=items.stream().map(DictionaryItem::getValue).collect(Collectors.toList()); initialNames=items.stream().map(DictionaryItem::getValue).collect(Collectors.toList());
} }
List<TicketSolution> solutions = lambdaQuery().eq(TicketSolution::getTicketId, ticketId).orderByAsc(TicketSolution::getId).list(); List<TicketSolution> solutions = lambdaQuery().eq(TicketSolution::getTicketId, ticketId).orderByAsc(TicketSolution::getId).list();
Map<String, List<TicketSolution>> groupedSolutions = solutions.stream().collect(Collectors.groupingBy(TicketSolution::getDictionaryName, LinkedHashMap::new, Collectors.toList())); Map<String, List<TicketSolution>> groupedSolutions = solutions.stream().collect(Collectors.groupingBy(TicketSolution::getDictionaryItemName, LinkedHashMap::new, Collectors.toList()));
if (CollectionUtil.isEmpty(groupedSolutions)) { if (CollectionUtil.isEmpty(groupedSolutions)) {
return initialNames.stream().map(ticketSolutions -> new SolutionMeasuresVO() return initialNames.stream().map(ticketSolutions -> new SolutionMeasuresVO()
.setName(ticketSolutions) .setName(ticketSolutions)
@ -66,7 +66,7 @@ public class TicketSolutionServiceImpl extends ServiceImpl<TicketSolutionMapper,
return groupedSolutions.entrySet().stream().map(ks -> new SolutionMeasuresVO() return groupedSolutions.entrySet().stream().map(ks -> new SolutionMeasuresVO()
.setName(ks.getKey()) .setName(ks.getKey())
.setItems(ks.getValue().stream().map(v -> new SolutionMeasuresItemVO() .setItems(ks.getValue().stream().map(v -> new SolutionMeasuresItemVO()
.setName(v.getDictionaryName()) .setName(v.getDescription())
.setSuperintendent(v.getSuperintendent()) .setSuperintendent(v.getSuperintendent())
.setScheduleDate(v.getScheduleDate()) .setScheduleDate(v.getScheduleDate())
.setConfirmedDate(v.getConfirmedDate()) .setConfirmedDate(v.getConfirmedDate())
@ -83,7 +83,7 @@ public class TicketSolutionServiceImpl extends ServiceImpl<TicketSolutionMapper,
vos.add(new SolutionMeasuresVO() vos.add(new SolutionMeasuresVO()
.setName(name) .setName(name)
.setItems(values.stream().map(v -> new SolutionMeasuresItemVO() .setItems(values.stream().map(v -> new SolutionMeasuresItemVO()
.setName(v.getDictionaryName()) .setName(v.getDescription())
.setSuperintendent(v.getSuperintendent()) .setSuperintendent(v.getSuperintendent())
.setScheduleDate(v.getScheduleDate()) .setScheduleDate(v.getScheduleDate())
.setConfirmedDate(v.getConfirmedDate()) .setConfirmedDate(v.getConfirmedDate())
@ -125,8 +125,9 @@ public class TicketSolutionServiceImpl extends ServiceImpl<TicketSolutionMapper,
for (SolutionMeasuresItemVO solutionMeasuresItemVO : solutionMeasuresVO.getItems()){ for (SolutionMeasuresItemVO solutionMeasuresItemVO : solutionMeasuresVO.getItems()){
TicketSolution solution=new TicketSolution() TicketSolution solution=new TicketSolution()
.setTicketId(request.getTicketId()) .setTicketId(request.getTicketId())
.setDictionaryId(solutionMeasuresVO.getId()) .setDictionaryItemId(solutionMeasuresVO.getId())
.setDictionaryName(solutionMeasuresVO.getName()) .setDictionaryItemName(solutionMeasuresVO.getName())
.setDescription(solutionMeasuresItemVO.getName())
.setSuperintendent(solutionMeasuresItemVO.getSuperintendent()) .setSuperintendent(solutionMeasuresItemVO.getSuperintendent())
.setScheduleDate(solutionMeasuresItemVO.getScheduleDate()) .setScheduleDate(solutionMeasuresItemVO.getScheduleDate())
.setConfirmedDate(solutionMeasuresItemVO.getConfirmedDate()); .setConfirmedDate(solutionMeasuresItemVO.getConfirmedDate());