|
|
@@ -19,11 +19,6 @@ import io.swagger.v3.oas.annotations.Operation;
|
|
|
import io.swagger.v3.oas.annotations.Parameter;
|
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
import liquibase.repackaged.org.apache.commons.lang3.StringUtils;
|
|
|
-import org.springframework.core.io.ByteArrayResource;
|
|
|
-import org.springframework.core.io.InputStreamResource;
|
|
|
-import org.springframework.http.HttpHeaders;
|
|
|
-import org.springframework.http.MediaType;
|
|
|
-import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.security.access.prepost.PreAuthorize;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
@@ -32,8 +27,8 @@ import javax.annotation.Resource;
|
|
|
import javax.annotation.security.PermitAll;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
import javax.validation.Valid;
|
|
|
-import java.io.ByteArrayInputStream;
|
|
|
import java.io.IOException;
|
|
|
+import java.io.OutputStream;
|
|
|
import java.util.List;
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
|
|
@@ -103,66 +98,57 @@ public class IotSocSummaryController {
|
|
|
BeanUtils.toBean(list, IotSocSummaryRespVO.class));
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- // 1. 下载接口:前端直接调用,触发文件下载
|
|
|
- @GetMapping("/safety-card/download/{id}")
|
|
|
+ @GetMapping("/safety-card/preview")
|
|
|
@PermitAll
|
|
|
- public ResponseEntity<ByteArrayResource> downloadSafetyCard(@PathVariable("id") Long id) throws Exception {
|
|
|
+ public void previewSafetyCard(Long id, HttpServletResponse response) throws Exception {
|
|
|
IotSocSummaryDO iotSocSummary = iotSocSummaryService.getIotSocSummary(id);
|
|
|
String person = iotSocSummary.getUserName();
|
|
|
String date = DateUtil.format(iotSocSummary.getObservationDate(), "yyyy-MM-dd HH:mm:ss");
|
|
|
String remark = iotSocSummary.getRemark();
|
|
|
- // 1. 构建生成参数(实际业务中需从前端/数据库获取checkItems)
|
|
|
- List<SafetyObservationCardGenerator.CheckItem> checkItems = buildCheckItems(); // 替换为实际业务逻辑
|
|
|
|
|
|
- // 1. 生成文件字节流(你原有逻辑)
|
|
|
+ List<SafetyObservationCardGenerator.CheckItem> checkItems = buildCheckItems();
|
|
|
byte[] docBytes = SafetyObservationCardGenerator.generateCardToBytes(checkItems, person, date, remark);
|
|
|
|
|
|
- // 2. 【关键修复】中文文件名必须 URL 编码!!!
|
|
|
- String fileName = "行为安全观察与沟通卡" + StringUtils.substring(date, 0, 10) + person + ".docx";
|
|
|
- String encodeFileName = URLEncodeUtil.encode(fileName); // Hutool编码
|
|
|
-
|
|
|
- // 3. 正确设置响应头
|
|
|
- HttpHeaders headers = new HttpHeaders();
|
|
|
- headers.add(HttpHeaders.CONTENT_DISPOSITION,
|
|
|
- "attachment; filename=\"" + encodeFileName + "\""); // 加引号更规范
|
|
|
- headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
|
|
-
|
|
|
- return ResponseEntity.ok()
|
|
|
- .headers(headers)
|
|
|
- .contentLength(docBytes.length)
|
|
|
- .body(new ByteArrayResource(docBytes));
|
|
|
+ // ========== 以下是 KKFileView 必须的标准格式 ==========
|
|
|
+ response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
|
|
|
+ response.setCharacterEncoding("UTF-8");
|
|
|
+ response.setContentLength(docBytes.length);
|
|
|
+ response.setHeader("Content-Disposition", "inline; filename=preview.docx");
|
|
|
+ response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
|
+ response.setHeader("Pragma", "no-cache");
|
|
|
+ response.setHeader("Expires", "0");
|
|
|
+
|
|
|
+ // 直接输出流(最稳定,不会被Spring包装坏)
|
|
|
+ try (OutputStream out = response.getOutputStream()) {
|
|
|
+ out.write(docBytes);
|
|
|
+ out.flush();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- // 2. 预览接口:供KKFileView调用(核心:返回文件流,不触发下载)
|
|
|
- @GetMapping("/safety-card/preview/{id}")
|
|
|
+ // 下载接口保持不变(也给你标准版本)
|
|
|
+ @GetMapping("/safety-card/download/{id}")
|
|
|
@PermitAll
|
|
|
- public ResponseEntity<InputStreamResource> previewSafetyCard(@PathVariable("id") Long id) throws Exception {
|
|
|
+ public void downloadSafetyCard(@PathVariable("id") Long id, HttpServletResponse response) throws Exception {
|
|
|
IotSocSummaryDO iotSocSummary = iotSocSummaryService.getIotSocSummary(id);
|
|
|
String person = iotSocSummary.getUserName();
|
|
|
String date = DateUtil.format(iotSocSummary.getObservationDate(), "yyyy-MM-dd HH:mm:ss");
|
|
|
String remark = iotSocSummary.getRemark();
|
|
|
- // 1. 构建生成参数
|
|
|
- List<SafetyObservationCardGenerator.CheckItem> checkItems = buildCheckItems(); // 替换为实际业务逻辑
|
|
|
|
|
|
- // 2. 动态生成文档字节数组
|
|
|
+ List<SafetyObservationCardGenerator.CheckItem> checkItems = buildCheckItems();
|
|
|
byte[] docBytes = SafetyObservationCardGenerator.generateCardToBytes(checkItems, person, date, remark);
|
|
|
- ByteArrayInputStream bais = new ByteArrayInputStream(docBytes);
|
|
|
- InputStreamResource resource = new InputStreamResource(bais);
|
|
|
-
|
|
|
- // 3. 设置响应头(关键:不设置attachment,让KKFileView能读取流)
|
|
|
- HttpHeaders headers = new HttpHeaders();
|
|
|
- headers.add(HttpHeaders.CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
|
|
|
- // 可选:设置缓存(根据业务需求)
|
|
|
- headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
|
-
|
|
|
- return ResponseEntity.ok()
|
|
|
- .headers(headers)
|
|
|
- .contentLength(docBytes.length)
|
|
|
- .body(resource);
|
|
|
- }
|
|
|
|
|
|
+ String fileName = URLEncodeUtil.encode("行为安全观察与沟通卡" + StringUtils.substring(date, 0, 10) + person + ".docx");
|
|
|
+
|
|
|
+ response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
|
|
|
+ response.setCharacterEncoding("UTF-8");
|
|
|
+ response.setContentLength(docBytes.length);
|
|
|
+ response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
|
|
|
+
|
|
|
+ try (OutputStream out = response.getOutputStream()) {
|
|
|
+ out.write(docBytes);
|
|
|
+ out.flush();
|
|
|
+ }
|
|
|
+ }
|
|
|
// 示例:构建检查项(实际业务中需从前端参数/数据库获取)
|
|
|
private List<SafetyObservationCardGenerator.CheckItem> buildCheckItems() {
|
|
|
return ImmutableList.of(
|